source: moodle/trunk/fuentes/admin/tool/uploadcourse/classes/helper.php @ 136

Last change on this file since 136 was 136, checked in by mabarracus, 4 years ago

Ported code to xenial

File size: 16.4 KB
Line 
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 * File containing the helper class.
19 *
20 * @package    tool_uploadcourse
21 * @copyright  2013 Frédéric Massart
22 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24
25defined('MOODLE_INTERNAL') || die();
26require_once($CFG->libdir . '/coursecatlib.php');
27require_once($CFG->dirroot . '/cache/lib.php');
28require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
29require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
30
31/**
32 * Class containing a set of helpers.
33 *
34 * @package    tool_uploadcourse
35 * @copyright  2013 Frédéric Massart
36 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37 */
38class tool_uploadcourse_helper {
39
40    /**
41     * Generate a shortname based on a template.
42     *
43     * @param array|object $data course data.
44     * @param string $templateshortname template of shortname.
45     * @return null|string shortname based on the template, or null when an error occured.
46     */
47    public static function generate_shortname($data, $templateshortname) {
48        if (empty($templateshortname) && !is_numeric($templateshortname)) {
49            return null;
50        }
51        if (strpos($templateshortname, '%') === false) {
52            return $templateshortname;
53        }
54
55        $course = (object) $data;
56        $fullname   = isset($course->fullname) ? $course->fullname : '';
57        $idnumber   = isset($course->idnumber) ? $course->idnumber  : '';
58
59        $callback = partial(array('tool_uploadcourse_helper', 'generate_shortname_callback'), $fullname, $idnumber);
60        $result = preg_replace_callback('/(?<!%)%([+~-])?(\d)*([fi])/', $callback, $templateshortname);
61
62        if (!is_null($result)) {
63            $result = clean_param($result, PARAM_TEXT);
64        }
65
66        if (empty($result) && !is_numeric($result)) {
67            $result = null;
68        }
69
70        return $result;
71    }
72
73    /**
74     * Callback used when generating a shortname based on a template.
75     *
76     * @param string $fullname full name.
77     * @param string $idnumber ID number.
78     * @param array $block result from preg_replace_callback.
79     * @return string
80     */
81    public static function generate_shortname_callback($fullname, $idnumber, $block) {
82        switch ($block[3]) {
83            case 'f':
84                $repl = $fullname;
85                break;
86            case 'i':
87                $repl = $idnumber;
88                break;
89            default:
90                return $block[0];
91        }
92
93        switch ($block[1]) {
94            case '+':
95                $repl = core_text::strtoupper($repl);
96                break;
97            case '-':
98                $repl = core_text::strtolower($repl);
99                break;
100            case '~':
101                $repl = core_text::strtotitle($repl);
102                break;
103        }
104
105        if (!empty($block[2])) {
106            $repl = core_text::substr($repl, 0, $block[2]);
107        }
108
109        return $repl;
110    }
111
112    /**
113     * Return the available course formats.
114     *
115     * @return array
116     */
117    public static function get_course_formats() {
118        return array_keys(core_component::get_plugin_list('format'));
119    }
120
121    /**
122     * Extract enrolment data from passed data.
123     *
124     * Constructs an array of methods, and their options:
125     * array(
126     *     'method1' => array(
127     *         'option1' => value,
128     *         'option2' => value
129     *     ),
130     *     'method2' => array(
131     *         'option1' => value,
132     *         'option2' => value
133     *     )
134     * )
135     *
136     * @param array $data data to extract the enrolment data from.
137     * @return array
138     */
139    public static function get_enrolment_data($data) {
140        $enrolmethods = array();
141        $enroloptions = array();
142        foreach ($data as $field => $value) {
143
144            // Enrolmnent data.
145            $matches = array();
146            if (preg_match('/^enrolment_(\d+)(_(.+))?$/', $field, $matches)) {
147                $key = $matches[1];
148                if (!isset($enroloptions[$key])) {
149                    $enroloptions[$key] = array();
150                }
151                if (empty($matches[3])) {
152                    $enrolmethods[$key] = $value;
153                } else {
154                    $enroloptions[$key][$matches[3]] = $value;
155                }
156            }
157        }
158
159        // Combining enrolment methods and their options in a single array.
160        $enrolmentdata = array();
161        if (!empty($enrolmethods)) {
162            $enrolmentplugins = self::get_enrolment_plugins();
163            foreach ($enrolmethods as $key => $method) {
164                if (!array_key_exists($method, $enrolmentplugins)) {
165                    // Error!
166                    continue;
167                }
168                $enrolmentdata[$enrolmethods[$key]] = $enroloptions[$key];
169            }
170        }
171        return $enrolmentdata;
172    }
173
174    /**
175     * Return the enrolment plugins.
176     *
177     * The result is cached for faster execution.
178     *
179     * @return array
180     */
181    public static function get_enrolment_plugins() {
182        $cache = cache::make('tool_uploadcourse', 'helper');
183        if (($enrol = $cache->get('enrol')) === false) {
184            $enrol = enrol_get_plugins(false);
185            $cache->set('enrol', $enrol);
186        }
187        return $enrol;
188    }
189
190    /**
191     * Get the restore content tempdir.
192     *
193     * The tempdir is the sub directory in which the backup has been extracted.
194     *
195     * This caches the result for better performance, but $CFG->keeptempdirectoriesonbackup
196     * needs to be enabled, otherwise the cache is ignored.
197     *
198     * @param string $backupfile path to a backup file.
199     * @param string $shortname shortname of a course.
200     * @param array $errors will be populated with errors found.
201     * @return string|false false when the backup couldn't retrieved.
202     */
203    public static function get_restore_content_dir($backupfile = null, $shortname = null, &$errors = array()) {
204        global $CFG, $DB, $USER;
205
206        $cachekey = null;
207        if (!empty($backupfile)) {
208            $backupfile = realpath($backupfile);
209            if (empty($backupfile) || !is_readable($backupfile)) {
210                $errors['cannotreadbackupfile'] = new lang_string('cannotreadbackupfile', 'tool_uploadcourse');
211                return false;
212            }
213            $cachekey = 'backup_path:' . $backupfile;
214        } else if (!empty($shortname) || is_numeric($shortname)) {
215            $cachekey = 'backup_sn:' . $shortname;
216        }
217
218        if (empty($cachekey)) {
219            return false;
220        }
221
222        // If $CFG->keeptempdirectoriesonbackup is not set to true, any restore happening would
223        // automatically delete the backup directory... causing the cache to return an unexisting directory.
224        $usecache = !empty($CFG->keeptempdirectoriesonbackup);
225        if ($usecache) {
226            $cache = cache::make('tool_uploadcourse', 'helper');
227        }
228
229        // If we don't use the cache, or if we do and not set, or the directory doesn't exist any more.
230        if (!$usecache || (($backupid = $cache->get($cachekey)) === false || !is_dir("$CFG->tempdir/backup/$backupid"))) {
231
232            // Use null instead of false because it would consider that the cache key has not been set.
233            $backupid = null;
234
235            if (!empty($backupfile)) {
236                // Extracting the backup file.
237                $packer = get_file_packer('application/vnd.moodle.backup');
238                $backupid = restore_controller::get_tempdir_name(SITEID, $USER->id);
239                $path = "$CFG->tempdir/backup/$backupid/";
240                $result = $packer->extract_to_pathname($backupfile, $path);
241                if (!$result) {
242                    $errors['invalidbackupfile'] = new lang_string('invalidbackupfile', 'tool_uploadcourse');
243                }
244            } else if (!empty($shortname) || is_numeric($shortname)) {
245                // Creating restore from an existing course.
246                $courseid = $DB->get_field('course', 'id', array('shortname' => $shortname), IGNORE_MISSING);
247                if (!empty($courseid)) {
248                    $bc = new backup_controller(backup::TYPE_1COURSE, $courseid, backup::FORMAT_MOODLE,
249                        backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
250                    $bc->execute_plan();
251                    $backupid = $bc->get_backupid();
252                    $bc->destroy();
253                } else {
254                    $errors['coursetorestorefromdoesnotexist'] =
255                        new lang_string('coursetorestorefromdoesnotexist', 'tool_uploadcourse');
256                }
257            }
258
259            if ($usecache) {
260                $cache->set($cachekey, $backupid);
261            }
262        }
263
264        if ($backupid === null) {
265            $backupid = false;
266        }
267        return $backupid;
268    }
269
270    /**
271     * Return the role IDs.
272     *
273     * The result is cached for faster execution.
274     *
275     * @return array
276     */
277    public static function get_role_ids() {
278        $cache = cache::make('tool_uploadcourse', 'helper');
279        if (($roles = $cache->get('roles')) === false) {
280            $roles = array();
281            $rolesraw = get_all_roles();
282            foreach ($rolesraw as $role) {
283                $roles[$role->shortname] = $role->id;
284            }
285            $cache->set('roles', $roles);
286        }
287        return $roles;
288    }
289
290    /**
291     * Get the role renaming data from the passed data.
292     *
293     * @param array $data data to extract the names from.
294     * @param array $errors will be populated with errors found.
295     * @return array where the key is the role_<id>, the value is the new name.
296     */
297    public static function get_role_names($data, &$errors = array()) {
298        $rolenames = array();
299        $rolesids = self::get_role_ids();
300        $invalidroles = array();
301        foreach ($data as $field => $value) {
302
303            $matches = array();
304            if (preg_match('/^role_(.+)?$/', $field, $matches)) {
305                if (!isset($rolesids[$matches[1]])) {
306                    $invalidroles[] = $matches[1];
307                    continue;
308                }
309                $rolenames['role_' . $rolesids[$matches[1]]] = $value;
310            }
311
312        }
313
314        if (!empty($invalidroles)) {
315            $errors['invalidroles'] = new lang_string('invalidroles', 'tool_uploadcourse', implode(', ', $invalidroles));
316        }
317
318        // Roles names.
319        return $rolenames;
320    }
321
322    /**
323     * Helper to increment an ID number.
324     *
325     * This first checks if the ID number is in use.
326     *
327     * @param string $idnumber ID number to increment.
328     * @return string new ID number.
329     */
330    public static function increment_idnumber($idnumber) {
331        global $DB;
332        while ($DB->record_exists('course', array('idnumber' => $idnumber))) {
333            $matches = array();
334            if (!preg_match('/(.*?)([0-9]+)$/', $idnumber, $matches)) {
335                $newidnumber = $idnumber . '_2';
336            } else {
337                $newidnumber = $matches[1] . ((int) $matches[2] + 1);
338            }
339            $idnumber = $newidnumber;
340        }
341        return $idnumber;
342    }
343
344    /**
345     * Helper to increment a shortname.
346     *
347     * This considers that the shortname passed has to be incremented.
348     *
349     * @param string $shortname shortname to increment.
350     * @return string new shortname.
351     */
352    public static function increment_shortname($shortname) {
353        global $DB;
354        do {
355            $matches = array();
356            if (!preg_match('/(.*?)([0-9]+)$/', $shortname, $matches)) {
357                $newshortname = $shortname . '_2';
358            } else {
359                $newshortname = $matches[1] . ($matches[2]+1);
360            }
361            $shortname = $newshortname;
362        } while ($DB->record_exists('course', array('shortname' => $shortname)));
363        return $shortname;
364    }
365
366    /**
367     * Resolve a category based on the data passed.
368     *
369     * Key accepted are:
370     * - category, which is supposed to be a category ID.
371     * - category_idnumber
372     * - category_path, array of categories from parent to child.
373     *
374     * @param array $data to resolve the category from.
375     * @param array $errors will be populated with errors found.
376     * @return int category ID.
377     */
378    public static function resolve_category($data, &$errors = array()) {
379        $catid = null;
380
381        if (!empty($data['category'])) {
382            $category = coursecat::get((int) $data['category'], IGNORE_MISSING);
383            if (!empty($category) && !empty($category->id)) {
384                $catid = $category->id;
385            } else {
386                $errors['couldnotresolvecatgorybyid'] =
387                    new lang_string('couldnotresolvecatgorybyid', 'tool_uploadcourse');
388            }
389        }
390
391        if (empty($catid) && !empty($data['category_idnumber'])) {
392            $catid = self::resolve_category_by_idnumber($data['category_idnumber']);
393            if (empty($catid)) {
394                $errors['couldnotresolvecatgorybyidnumber'] =
395                    new lang_string('couldnotresolvecatgorybyidnumber', 'tool_uploadcourse');
396            }
397        }
398        if (empty($catid) && !empty($data['category_path'])) {
399            $catid = self::resolve_category_by_path(explode(' / ', $data['category_path']));
400            if (empty($catid)) {
401                $errors['couldnotresolvecatgorybypath'] =
402                    new lang_string('couldnotresolvecatgorybypath', 'tool_uploadcourse');
403            }
404        }
405
406        return $catid;
407    }
408
409    /**
410     * Resolve a category by ID number.
411     *
412     * @param string $idnumber category ID number.
413     * @return int category ID.
414     */
415    public static function resolve_category_by_idnumber($idnumber) {
416        global $DB;
417        $cache = cache::make('tool_uploadcourse', 'helper');
418        $cachekey = 'cat_idn_' . $idnumber;
419        if (($id = $cache->get($cachekey)) === false) {
420            $params = array('idnumber' => $idnumber);
421            $id = $DB->get_field_select('course_categories', 'id', 'idnumber = :idnumber', $params, IGNORE_MISSING);
422
423            // Little hack to be able to differenciate between the cache not set and a category not found.
424            if ($id === false) {
425                $id = -1;
426            }
427
428            $cache->set($cachekey, $id);
429        }
430
431        // Little hack to be able to differenciate between the cache not set and a category not found.
432        if ($id == -1) {
433            $id = false;
434        }
435
436        return $id;
437    }
438
439    /**
440     * Resolve a category by path.
441     *
442     * @param array $path category names indexed from parent to children.
443     * @return int category ID.
444     */
445    public static function resolve_category_by_path(array $path) {
446        global $DB;
447        $cache = cache::make('tool_uploadcourse', 'helper');
448        $cachekey = 'cat_path_' . serialize($path);
449        if (($id = $cache->get($cachekey)) === false) {
450            $parent = 0;
451            $sql = 'name = :name AND parent = :parent';
452            while ($name = array_shift($path)) {
453                $params = array('name' => $name, 'parent' => $parent);
454                if ($records = $DB->get_records_select('course_categories', $sql, $params, null, 'id, parent')) {
455                    if (count($records) > 1) {
456                        // Too many records with the same name!
457                        $id = -1;
458                        break;
459                    }
460                    $record = reset($records);
461                    $id = $record->id;
462                    $parent = $record->id;
463                } else {
464                    // Not found.
465                    $id = -1;
466                    break;
467                }
468            }
469            $cache->set($cachekey, $id);
470        }
471
472        // We save -1 when the category has not been found to be able to know if the cache was set.
473        if ($id == -1) {
474            $id = false;
475        }
476        return $id;
477    }
478
479}
Note: See TracBrowser for help on using the repository browser.