See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 402] [Versions 401 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Module generator base class. 19 * 20 * @package core 21 * @category test 22 * @copyright 2012 Petr Skoda {@link http://skodak.org} 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 /** 29 * Module generator base class. 30 * 31 * Extend in mod/xxxx/tests/generator/lib.php as class mod_xxxx_generator. 32 * 33 * @package core 34 * @category test 35 * @copyright 2012 Petr Skoda {@link http://skodak.org} 36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 */ 38 abstract class testing_module_generator extends component_generator_base { 39 40 /** 41 * @var number of created instances 42 */ 43 protected $instancecount = 0; 44 45 /** 46 * To be called from data reset code only, 47 * do not use in tests. 48 * @return void 49 */ 50 public function reset() { 51 $this->instancecount = 0; 52 } 53 54 /** 55 * Returns module name 56 * @return string name of module that this class describes 57 * @throws coding_exception if class invalid 58 */ 59 public function get_modulename() { 60 $matches = null; 61 if (!preg_match('/^mod_([a-z0-9]+)_generator$/', get_class($this), $matches)) { 62 throw new coding_exception('Invalid module generator class name: '.get_class($this)); 63 } 64 65 if (empty($matches[1])) { 66 throw new coding_exception('Invalid module generator class name: '.get_class($this)); 67 } 68 return $matches[1]; 69 } 70 71 /** 72 * Create course module and link it to course 73 * 74 * Since 2.6 it is recommended to use function add_moduleinfo() to create a module. 75 * 76 * @deprecated since 2.6 77 * @see testing_module_generator::create_instance() 78 * 79 * @param integer $courseid 80 * @param array $options section, visible 81 * @return integer $cm instance id 82 */ 83 protected function precreate_course_module($courseid, array $options) { 84 global $DB, $CFG; 85 require_once("$CFG->dirroot/course/lib.php"); 86 87 $modulename = $this->get_modulename(); 88 $sectionnum = isset($options['section']) ? $options['section'] : 0; 89 unset($options['section']); // Prevent confusion, it would be overridden later in course_add_cm_to_section() anyway. 90 91 $cm = new stdClass(); 92 $cm->course = $courseid; 93 $cm->module = $DB->get_field('modules', 'id', array('name'=>$modulename)); 94 $cm->instance = 0; 95 $cm->section = 0; 96 $cm->idnumber = isset($options['idnumber']) ? $options['idnumber'] : 0; 97 $cm->added = time(); 98 99 $columns = $DB->get_columns('course_modules'); 100 foreach ($options as $key => $value) { 101 if ($key === 'id' or !isset($columns[$key])) { 102 continue; 103 } 104 if (property_exists($cm, $key)) { 105 continue; 106 } 107 $cm->$key = $value; 108 } 109 110 $cm->id = $DB->insert_record('course_modules', $cm); 111 112 course_add_cm_to_section($courseid, $cm->id, $sectionnum); 113 114 return $cm->id; 115 } 116 117 /** 118 * Called after *_add_instance() 119 * 120 * Since 2.6 it is recommended to use function add_moduleinfo() to create a module. 121 * 122 * @deprecated since 2.6 123 * @see testing_module_generator::create_instance() 124 * 125 * @param int $id 126 * @param int $cmid 127 * @return stdClass module instance 128 */ 129 protected function post_add_instance($id, $cmid) { 130 global $DB; 131 132 $DB->set_field('course_modules', 'instance', $id, array('id'=>$cmid)); 133 134 $instance = $DB->get_record($this->get_modulename(), array('id'=>$id), '*', MUST_EXIST); 135 136 $cm = get_coursemodule_from_id($this->get_modulename(), $cmid, $instance->course, true, MUST_EXIST); 137 context_module::instance($cm->id); 138 139 $instance->cmid = $cm->id; 140 141 return $instance; 142 } 143 144 /** 145 * Merges together arguments $record and $options and fills default module 146 * fields that are shared by all module types 147 * 148 * @param object|array $record fields (different from defaults) for this module 149 * @param null|array $options for backward-compatibility this may include fields from course_modules 150 * table. They are merged into $record 151 * @throws coding_exception if $record->course is not specified 152 */ 153 protected function prepare_moduleinfo_record($record, $options) { 154 global $DB; 155 // Make sure we don't modify the original object. 156 $moduleinfo = (object)(array)$record; 157 158 if (empty($moduleinfo->course)) { 159 throw new coding_exception('module generator requires $record->course'); 160 } 161 162 $moduleinfo->modulename = $this->get_modulename(); 163 $moduleinfo->module = $DB->get_field('modules', 'id', array('name' => $moduleinfo->modulename)); 164 165 // Allow idnumber to be set as either $options['idnumber'] or $moduleinfo->cmidnumber or $moduleinfo->idnumber. 166 // The actual field name is 'idnumber' but add_moduleinfo() expects 'cmidnumber'. 167 if (isset($options['idnumber'])) { 168 $moduleinfo->cmidnumber = $options['idnumber']; 169 } else if (!isset($moduleinfo->cmidnumber) && isset($moduleinfo->idnumber)) { 170 $moduleinfo->cmidnumber = $moduleinfo->idnumber; 171 } 172 173 // These are the fields from table 'course_modules' in 2.6 when the second 174 // argument $options is being deprecated. 175 // List excludes fields: instance (does not exist yet), course, module and idnumber (set above) 176 $easymergefields = array('section', 'added', 'score', 'indent', 177 'visible', 'visibleold', 'groupmode', 'groupingid', 178 'completion', 'completiongradeitemnumber', 'completionview', 'completionexpected', 179 'completionpassgrade', 'availability', 'showdescription'); 180 foreach ($easymergefields as $key) { 181 if (isset($options[$key])) { 182 $moduleinfo->$key = $options[$key]; 183 } 184 } 185 186 // Set default values. Note that visibleold and completiongradeitemnumber are not used when creating a module. 187 $defaults = array( 188 'section' => 0, 189 'visible' => 1, 190 'visibleoncoursepage' => 1, 191 'cmidnumber' => '', 192 'groupmode' => 0, 193 'groupingid' => 0, 194 'availability' => null, 195 'completion' => 0, 196 'completionview' => 0, 197 'completionexpected' => 0, 198 'completionpassgrade' => 0, 199 'conditiongradegroup' => array(), 200 'conditionfieldgroup' => array(), 201 'conditioncompletiongroup' => array() 202 ); 203 204 foreach ($defaults as $key => $value) { 205 if (!isset($moduleinfo->$key)) { 206 $moduleinfo->$key = $value; 207 } 208 } 209 210 return $moduleinfo; 211 } 212 213 /** 214 * Creates an instance of the module for testing purposes. 215 * 216 * Module type will be taken from the class name. Each module type may overwrite 217 * this function to add other default values used by it. 218 * 219 * @param array|stdClass $record data for module being generated. Requires 'course' key 220 * (an id or the full object). Also can have any fields from add module form. 221 * @param null|array $options general options for course module. Since 2.6 it is 222 * possible to omit this argument by merging options into $record 223 * @return stdClass record from module-defined table with additional field 224 * cmid (corresponding id in course_modules table) 225 */ 226 public function create_instance($record = null, array $options = null) { 227 global $CFG, $DB, $PAGE; 228 require_once($CFG->dirroot.'/course/modlib.php'); 229 230 $this->instancecount++; 231 232 // Creating an activity is a back end operation, which should not cause any output to happen. 233 // This will allow us to check that the theme was not initialised while creating the module instance. 234 $outputstartedbefore = $PAGE->get_where_theme_was_initialised(); 235 236 // Merge options into record and add default values. 237 $record = $this->prepare_moduleinfo_record($record, $options); 238 239 // Retrieve the course record. 240 if (!empty($record->course->id)) { 241 $course = $record->course; 242 $record->course = $record->course->id; 243 } else { 244 $course = get_course($record->course); 245 } 246 247 // Fill the name and intro with default values (if missing). 248 if (empty($record->name)) { 249 $record->name = get_string('pluginname', $this->get_modulename()).' '.$this->instancecount; 250 } 251 if (empty($record->introeditor) && empty($record->intro)) { 252 $record->intro = 'Test '.$this->get_modulename().' ' . $this->instancecount; 253 } 254 if (empty($record->introeditor) && empty($record->introformat)) { 255 $record->introformat = FORMAT_MOODLE; 256 } 257 258 if (isset($record->tags) && !is_array($record->tags)) { 259 $record->tags = preg_split('/\s*,\s*/', trim($record->tags), -1, PREG_SPLIT_NO_EMPTY); 260 } 261 262 // Before Moodle 2.6 it was possible to create a module with completion tracking when 263 // it is not setup for course and/or site-wide. Display debugging message so it is 264 // easier to trace an error in unittests. 265 if ($record->completion && empty($CFG->enablecompletion)) { 266 debugging('Did you forget to set $CFG->enablecompletion before generating module with completion tracking?', DEBUG_DEVELOPER); 267 } 268 if ($record->completion && empty($course->enablecompletion)) { 269 debugging('Did you forget to enable completion tracking for the course before generating module with completion tracking?', DEBUG_DEVELOPER); 270 } 271 272 if (!empty($record->lang) && !has_capability('moodle/course:setforcedlanguage', context_course::instance($course->id))) { 273 throw new coding_exception('Attempt to generate an activity when the current user does not have ' . 274 'permission moodle/course:setforcedlanguage. This does not work.'); 275 } 276 277 // Add the module to the course. 278 $moduleinfo = add_moduleinfo($record, $course); 279 280 // Prepare object to return with additional field cmid. 281 $modulename = $this->get_modulename(); 282 $instance = $DB->get_record($modulename, ['id' => $moduleinfo->instance], '*', MUST_EXIST); 283 $instance->cmid = $moduleinfo->coursemodule; 284 285 // Insert files for the 'intro' file area. 286 $instance = $this->insert_files( 287 $instance, 288 $record, 289 $modulename, 290 \context_module::instance($instance->cmid), 291 "mod_{$modulename}", 292 'intro', 293 0 294 ); 295 296 // If the theme was initialised while creating the module instance, something somewhere called an output 297 // function. Rather than leaving this as a hard-to-debug situation, let's make it fail with a clear error. 298 $outputstartedafter = $PAGE->get_where_theme_was_initialised(); 299 300 if ($outputstartedbefore === null && $outputstartedafter !== null) { 301 throw new coding_exception('Creating a mod_' . $this->get_modulename() . ' activity initialised the theme and output!', 302 'This should not happen. Creating an activity should be a pure back-end operation. Unnecessarily initialising ' . 303 'the output mechanism at the wrong time can cause subtle bugs and is a significant performance hit. There is ' . 304 'likely a call to an output function that caused it:' . PHP_EOL . PHP_EOL . 305 format_backtrace($outputstartedafter, true)); 306 } 307 308 return $instance; 309 } 310 311 /** 312 * Generates a piece of content for the module. 313 * User is usually taken from global $USER variable. 314 * @param stdClass $instance object returned from create_instance() call 315 * @param stdClass|array $record 316 * @return stdClass generated object 317 * @throws coding_exception if function is not implemented by module 318 */ 319 public function create_content($instance, $record = array()) { 320 throw new coding_exception('Module generator for '.$this->get_modulename().' does not implement method create_content()'); 321 } 322 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body