Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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 int 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
            // Module label can be created without name specified. It will get its name from the intro's text.
251
            if ($this->get_modulename() === 'label') {
252
                $record->name = '';
253
            }
254
        }
255
        if (empty($record->introeditor) && empty($record->intro)) {
256
            $record->intro = 'Test '.$this->get_modulename().' ' . $this->instancecount;
257
        }
258
        if (empty($record->introeditor) && empty($record->introformat)) {
259
            $record->introformat = FORMAT_MOODLE;
260
        }
261
 
262
        if (isset($record->tags) && !is_array($record->tags)) {
263
            $record->tags = preg_split('/\s*,\s*/', trim($record->tags), -1, PREG_SPLIT_NO_EMPTY);
264
        }
265
 
266
        // Before Moodle 2.6 it was possible to create a module with completion tracking when
267
        // it is not setup for course and/or site-wide. Display debugging message so it is
268
        // easier to trace an error in unittests.
269
        if ($record->completion && empty($CFG->enablecompletion)) {
270
            debugging('Did you forget to set $CFG->enablecompletion before generating module with completion tracking?', DEBUG_DEVELOPER);
271
        }
272
        if ($record->completion && empty($course->enablecompletion)) {
273
            debugging('Did you forget to enable completion tracking for the course before generating module with completion tracking?', DEBUG_DEVELOPER);
274
        }
275
 
276
        if (!empty($record->lang) && !has_capability('moodle/course:setforcedlanguage', context_course::instance($course->id))) {
277
            throw new coding_exception('Attempt to generate an activity when the current user does not have ' .
278
                    'permission moodle/course:setforcedlanguage. This does not work.');
279
        }
280
 
281
        // Add the module to the course.
282
        $moduleinfo = add_moduleinfo($record, $course);
283
 
284
        // Prepare object to return with additional field cmid.
285
        $modulename = $this->get_modulename();
286
        $instance = $DB->get_record($modulename, ['id' => $moduleinfo->instance], '*', MUST_EXIST);
287
        $instance->cmid = $moduleinfo->coursemodule;
288
 
289
        // Insert files for the 'intro' file area.
290
        $instance = $this->insert_files(
291
            $instance,
292
            $record,
293
            $modulename,
294
            \context_module::instance($instance->cmid),
295
            "mod_{$modulename}",
296
            'intro',
297
 
298
        );
299
 
300
        // If the theme was initialised while creating the module instance, something somewhere called an output
301
        // function. Rather than leaving this as a hard-to-debug situation, let's make it fail with a clear error.
302
        $outputstartedafter = $PAGE->get_where_theme_was_initialised();
303
 
304
        if ($outputstartedbefore === null && $outputstartedafter !== null) {
305
            throw new coding_exception('Creating a mod_' . $this->get_modulename() . ' activity initialised the theme and output!',
306
                'This should not happen. Creating an activity should be a pure back-end operation. Unnecessarily initialising ' .
307
                'the output mechanism at the wrong time can cause subtle bugs and is a significant performance hit. There is ' .
308
                'likely a call to an output function that caused it:' . PHP_EOL . PHP_EOL .
309
                format_backtrace($outputstartedafter, true));
310
        }
311
 
312
        return $instance;
313
    }
314
 
315
    /**
316
     * Generates a piece of content for the module.
317
     * User is usually taken from global $USER variable.
318
     * @param stdClass $instance object returned from create_instance() call
319
     * @param stdClass|array $record
320
     * @return stdClass generated object
321
     * @throws coding_exception if function is not implemented by module
322
     */
323
    public function create_content($instance, $record = array()) {
324
        throw new coding_exception('Module generator for '.$this->get_modulename().' does not implement method create_content()');
325
    }
326
}