PageRenderTime 64ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/admin/tool/uploadcourse/classes/helper.php

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