PageRenderTime 59ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/enrollib.php

http://github.com/moodle/moodle
PHP | 3244 lines | 1814 code | 415 blank | 1015 comment | 330 complexity | 9494a44d2a403c7e8a84f4dd3f0303f0 MD5 | raw file
Possible License(s): MIT, AGPL-3.0, MPL-2.0-no-copyleft-exception, LGPL-3.0, GPL-3.0, Apache-2.0, LGPL-2.1, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  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. * This library includes the basic parts of enrol api.
  18. * It is available on each page.
  19. *
  20. * @package core
  21. * @subpackage enrol
  22. * @copyright 2010 Petr Skoda {@link http://skodak.org}
  23. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24. */
  25. defined('MOODLE_INTERNAL') || die();
  26. /** Course enrol instance enabled. (used in enrol->status) */
  27. define('ENROL_INSTANCE_ENABLED', 0);
  28. /** Course enrol instance disabled, user may enter course if other enrol instance enabled. (used in enrol->status)*/
  29. define('ENROL_INSTANCE_DISABLED', 1);
  30. /** User is active participant (used in user_enrolments->status)*/
  31. define('ENROL_USER_ACTIVE', 0);
  32. /** User participation in course is suspended (used in user_enrolments->status) */
  33. define('ENROL_USER_SUSPENDED', 1);
  34. /** @deprecated - enrol caching was reworked, use ENROL_MAX_TIMESTAMP instead */
  35. define('ENROL_REQUIRE_LOGIN_CACHE_PERIOD', 1800);
  36. /** The timestamp indicating forever */
  37. define('ENROL_MAX_TIMESTAMP', 2147483647);
  38. /** When user disappears from external source, the enrolment is completely removed */
  39. define('ENROL_EXT_REMOVED_UNENROL', 0);
  40. /** When user disappears from external source, the enrolment is kept as is - one way sync */
  41. define('ENROL_EXT_REMOVED_KEEP', 1);
  42. /** @deprecated since 2.4 not used any more, migrate plugin to new restore methods */
  43. define('ENROL_RESTORE_TYPE', 'enrolrestore');
  44. /**
  45. * When user disappears from external source, user enrolment is suspended, roles are kept as is.
  46. * In some cases user needs a role with some capability to be visible in UI - suc has in gradebook,
  47. * assignments, etc.
  48. */
  49. define('ENROL_EXT_REMOVED_SUSPEND', 2);
  50. /**
  51. * When user disappears from external source, the enrolment is suspended and roles assigned
  52. * by enrol instance are removed. Please note that user may "disappear" from gradebook and other areas.
  53. * */
  54. define('ENROL_EXT_REMOVED_SUSPENDNOROLES', 3);
  55. /**
  56. * Do not send email.
  57. */
  58. define('ENROL_DO_NOT_SEND_EMAIL', 0);
  59. /**
  60. * Send email from course contact.
  61. */
  62. define('ENROL_SEND_EMAIL_FROM_COURSE_CONTACT', 1);
  63. /**
  64. * Send email from enrolment key holder.
  65. */
  66. define('ENROL_SEND_EMAIL_FROM_KEY_HOLDER', 2);
  67. /**
  68. * Send email from no reply address.
  69. */
  70. define('ENROL_SEND_EMAIL_FROM_NOREPLY', 3);
  71. /** Edit enrolment action. */
  72. define('ENROL_ACTION_EDIT', 'editenrolment');
  73. /** Unenrol action. */
  74. define('ENROL_ACTION_UNENROL', 'unenrol');
  75. /**
  76. * Returns instances of enrol plugins
  77. * @param bool $enabled return enabled only
  78. * @return array of enrol plugins name=>instance
  79. */
  80. function enrol_get_plugins($enabled) {
  81. global $CFG;
  82. $result = array();
  83. if ($enabled) {
  84. // sorted by enabled plugin order
  85. $enabled = explode(',', $CFG->enrol_plugins_enabled);
  86. $plugins = array();
  87. foreach ($enabled as $plugin) {
  88. $plugins[$plugin] = "$CFG->dirroot/enrol/$plugin";
  89. }
  90. } else {
  91. // sorted alphabetically
  92. $plugins = core_component::get_plugin_list('enrol');
  93. ksort($plugins);
  94. }
  95. foreach ($plugins as $plugin=>$location) {
  96. $class = "enrol_{$plugin}_plugin";
  97. if (!class_exists($class)) {
  98. if (!file_exists("$location/lib.php")) {
  99. continue;
  100. }
  101. include_once("$location/lib.php");
  102. if (!class_exists($class)) {
  103. continue;
  104. }
  105. }
  106. $result[$plugin] = new $class();
  107. }
  108. return $result;
  109. }
  110. /**
  111. * Returns instance of enrol plugin
  112. * @param string $name name of enrol plugin ('manual', 'guest', ...)
  113. * @return enrol_plugin
  114. */
  115. function enrol_get_plugin($name) {
  116. global $CFG;
  117. $name = clean_param($name, PARAM_PLUGIN);
  118. if (empty($name)) {
  119. // ignore malformed or missing plugin names completely
  120. return null;
  121. }
  122. $location = "$CFG->dirroot/enrol/$name";
  123. $class = "enrol_{$name}_plugin";
  124. if (!class_exists($class)) {
  125. if (!file_exists("$location/lib.php")) {
  126. return null;
  127. }
  128. include_once("$location/lib.php");
  129. if (!class_exists($class)) {
  130. return null;
  131. }
  132. }
  133. return new $class();
  134. }
  135. /**
  136. * Returns enrolment instances in given course.
  137. * @param int $courseid
  138. * @param bool $enabled
  139. * @return array of enrol instances
  140. */
  141. function enrol_get_instances($courseid, $enabled) {
  142. global $DB, $CFG;
  143. if (!$enabled) {
  144. return $DB->get_records('enrol', array('courseid'=>$courseid), 'sortorder,id');
  145. }
  146. $result = $DB->get_records('enrol', array('courseid'=>$courseid, 'status'=>ENROL_INSTANCE_ENABLED), 'sortorder,id');
  147. $enabled = explode(',', $CFG->enrol_plugins_enabled);
  148. foreach ($result as $key=>$instance) {
  149. if (!in_array($instance->enrol, $enabled)) {
  150. unset($result[$key]);
  151. continue;
  152. }
  153. if (!file_exists("$CFG->dirroot/enrol/$instance->enrol/lib.php")) {
  154. // broken plugin
  155. unset($result[$key]);
  156. continue;
  157. }
  158. }
  159. return $result;
  160. }
  161. /**
  162. * Checks if a given plugin is in the list of enabled enrolment plugins.
  163. *
  164. * @param string $enrol Enrolment plugin name
  165. * @return boolean Whether the plugin is enabled
  166. */
  167. function enrol_is_enabled($enrol) {
  168. global $CFG;
  169. if (empty($CFG->enrol_plugins_enabled)) {
  170. return false;
  171. }
  172. return in_array($enrol, explode(',', $CFG->enrol_plugins_enabled));
  173. }
  174. /**
  175. * Check all the login enrolment information for the given user object
  176. * by querying the enrolment plugins
  177. *
  178. * This function may be very slow, use only once after log-in or login-as.
  179. *
  180. * @param stdClass $user
  181. * @return void
  182. */
  183. function enrol_check_plugins($user) {
  184. global $CFG;
  185. if (empty($user->id) or isguestuser($user)) {
  186. // shortcut - there is no enrolment work for guests and not-logged-in users
  187. return;
  188. }
  189. // originally there was a broken admin test, but accidentally it was non-functional in 2.2,
  190. // which proved it was actually not necessary.
  191. static $inprogress = array(); // To prevent this function being called more than once in an invocation
  192. if (!empty($inprogress[$user->id])) {
  193. return;
  194. }
  195. $inprogress[$user->id] = true; // Set the flag
  196. $enabled = enrol_get_plugins(true);
  197. foreach($enabled as $enrol) {
  198. $enrol->sync_user_enrolments($user);
  199. }
  200. unset($inprogress[$user->id]); // Unset the flag
  201. }
  202. /**
  203. * Do these two students share any course?
  204. *
  205. * The courses has to be visible and enrolments has to be active,
  206. * timestart and timeend restrictions are ignored.
  207. *
  208. * This function calls {@see enrol_get_shared_courses()} setting checkexistsonly
  209. * to true.
  210. *
  211. * @param stdClass|int $user1
  212. * @param stdClass|int $user2
  213. * @return bool
  214. */
  215. function enrol_sharing_course($user1, $user2) {
  216. return enrol_get_shared_courses($user1, $user2, false, true);
  217. }
  218. /**
  219. * Returns any courses shared by the two users
  220. *
  221. * The courses has to be visible and enrolments has to be active,
  222. * timestart and timeend restrictions are ignored.
  223. *
  224. * @global moodle_database $DB
  225. * @param stdClass|int $user1
  226. * @param stdClass|int $user2
  227. * @param bool $preloadcontexts If set to true contexts for the returned courses
  228. * will be preloaded.
  229. * @param bool $checkexistsonly If set to true then this function will return true
  230. * if the users share any courses and false if not.
  231. * @return array|bool An array of courses that both users are enrolled in OR if
  232. * $checkexistsonly set returns true if the users share any courses
  233. * and false if not.
  234. */
  235. function enrol_get_shared_courses($user1, $user2, $preloadcontexts = false, $checkexistsonly = false) {
  236. global $DB, $CFG;
  237. $user1 = isset($user1->id) ? $user1->id : $user1;
  238. $user2 = isset($user2->id) ? $user2->id : $user2;
  239. if (empty($user1) or empty($user2)) {
  240. return false;
  241. }
  242. if (!$plugins = explode(',', $CFG->enrol_plugins_enabled)) {
  243. return false;
  244. }
  245. list($plugins1, $params1) = $DB->get_in_or_equal($plugins, SQL_PARAMS_NAMED, 'ee1');
  246. list($plugins2, $params2) = $DB->get_in_or_equal($plugins, SQL_PARAMS_NAMED, 'ee2');
  247. $params = array_merge($params1, $params2);
  248. $params['enabled1'] = ENROL_INSTANCE_ENABLED;
  249. $params['enabled2'] = ENROL_INSTANCE_ENABLED;
  250. $params['active1'] = ENROL_USER_ACTIVE;
  251. $params['active2'] = ENROL_USER_ACTIVE;
  252. $params['user1'] = $user1;
  253. $params['user2'] = $user2;
  254. $ctxselect = '';
  255. $ctxjoin = '';
  256. if ($preloadcontexts) {
  257. $ctxselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
  258. $ctxjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel)";
  259. $params['contextlevel'] = CONTEXT_COURSE;
  260. }
  261. $sql = "SELECT c.* $ctxselect
  262. FROM {course} c
  263. JOIN (
  264. SELECT DISTINCT c.id
  265. FROM {course} c
  266. JOIN {enrol} e1 ON (c.id = e1.courseid AND e1.status = :enabled1 AND e1.enrol $plugins1)
  267. JOIN {user_enrolments} ue1 ON (ue1.enrolid = e1.id AND ue1.status = :active1 AND ue1.userid = :user1)
  268. JOIN {enrol} e2 ON (c.id = e2.courseid AND e2.status = :enabled2 AND e2.enrol $plugins2)
  269. JOIN {user_enrolments} ue2 ON (ue2.enrolid = e2.id AND ue2.status = :active2 AND ue2.userid = :user2)
  270. WHERE c.visible = 1
  271. ) ec ON ec.id = c.id
  272. $ctxjoin";
  273. if ($checkexistsonly) {
  274. return $DB->record_exists_sql($sql, $params);
  275. } else {
  276. $courses = $DB->get_records_sql($sql, $params);
  277. if ($preloadcontexts) {
  278. array_map('context_helper::preload_from_record', $courses);
  279. }
  280. return $courses;
  281. }
  282. }
  283. /**
  284. * This function adds necessary enrol plugins UI into the course edit form.
  285. *
  286. * @param MoodleQuickForm $mform
  287. * @param object $data course edit form data
  288. * @param object $context context of existing course or parent category if course does not exist
  289. * @return void
  290. */
  291. function enrol_course_edit_form(MoodleQuickForm $mform, $data, $context) {
  292. $plugins = enrol_get_plugins(true);
  293. if (!empty($data->id)) {
  294. $instances = enrol_get_instances($data->id, false);
  295. foreach ($instances as $instance) {
  296. if (!isset($plugins[$instance->enrol])) {
  297. continue;
  298. }
  299. $plugin = $plugins[$instance->enrol];
  300. $plugin->course_edit_form($instance, $mform, $data, $context);
  301. }
  302. } else {
  303. foreach ($plugins as $plugin) {
  304. $plugin->course_edit_form(NULL, $mform, $data, $context);
  305. }
  306. }
  307. }
  308. /**
  309. * Validate course edit form data
  310. *
  311. * @param array $data raw form data
  312. * @param object $context context of existing course or parent category if course does not exist
  313. * @return array errors array
  314. */
  315. function enrol_course_edit_validation(array $data, $context) {
  316. $errors = array();
  317. $plugins = enrol_get_plugins(true);
  318. if (!empty($data['id'])) {
  319. $instances = enrol_get_instances($data['id'], false);
  320. foreach ($instances as $instance) {
  321. if (!isset($plugins[$instance->enrol])) {
  322. continue;
  323. }
  324. $plugin = $plugins[$instance->enrol];
  325. $errors = array_merge($errors, $plugin->course_edit_validation($instance, $data, $context));
  326. }
  327. } else {
  328. foreach ($plugins as $plugin) {
  329. $errors = array_merge($errors, $plugin->course_edit_validation(NULL, $data, $context));
  330. }
  331. }
  332. return $errors;
  333. }
  334. /**
  335. * Update enrol instances after course edit form submission
  336. * @param bool $inserted true means new course added, false course already existed
  337. * @param object $course
  338. * @param object $data form data
  339. * @return void
  340. */
  341. function enrol_course_updated($inserted, $course, $data) {
  342. global $DB, $CFG;
  343. $plugins = enrol_get_plugins(true);
  344. foreach ($plugins as $plugin) {
  345. $plugin->course_updated($inserted, $course, $data);
  346. }
  347. }
  348. /**
  349. * Add navigation nodes
  350. * @param navigation_node $coursenode
  351. * @param object $course
  352. * @return void
  353. */
  354. function enrol_add_course_navigation(navigation_node $coursenode, $course) {
  355. global $CFG;
  356. $coursecontext = context_course::instance($course->id);
  357. $instances = enrol_get_instances($course->id, true);
  358. $plugins = enrol_get_plugins(true);
  359. // we do not want to break all course pages if there is some borked enrol plugin, right?
  360. foreach ($instances as $k=>$instance) {
  361. if (!isset($plugins[$instance->enrol])) {
  362. unset($instances[$k]);
  363. }
  364. }
  365. $usersnode = $coursenode->add(get_string('users'), null, navigation_node::TYPE_CONTAINER, null, 'users');
  366. if ($course->id != SITEID) {
  367. // list all participants - allows assigning roles, groups, etc.
  368. if (has_capability('moodle/course:enrolreview', $coursecontext)) {
  369. $url = new moodle_url('/user/index.php', array('id'=>$course->id));
  370. $usersnode->add(get_string('enrolledusers', 'enrol'), $url, navigation_node::TYPE_SETTING, null, 'review', new pix_icon('i/enrolusers', ''));
  371. }
  372. // manage enrol plugin instances
  373. if (has_capability('moodle/course:enrolconfig', $coursecontext) or has_capability('moodle/course:enrolreview', $coursecontext)) {
  374. $url = new moodle_url('/enrol/instances.php', array('id'=>$course->id));
  375. } else {
  376. $url = NULL;
  377. }
  378. $instancesnode = $usersnode->add(get_string('enrolmentinstances', 'enrol'), $url, navigation_node::TYPE_SETTING, null, 'manageinstances');
  379. // each instance decides how to configure itself or how many other nav items are exposed
  380. foreach ($instances as $instance) {
  381. if (!isset($plugins[$instance->enrol])) {
  382. continue;
  383. }
  384. $plugins[$instance->enrol]->add_course_navigation($instancesnode, $instance);
  385. }
  386. if (!$url) {
  387. $instancesnode->trim_if_empty();
  388. }
  389. }
  390. // Manage groups in this course or even frontpage
  391. if (($course->groupmode || !$course->groupmodeforce) && has_capability('moodle/course:managegroups', $coursecontext)) {
  392. $url = new moodle_url('/group/index.php', array('id'=>$course->id));
  393. $usersnode->add(get_string('groups'), $url, navigation_node::TYPE_SETTING, null, 'groups', new pix_icon('i/group', ''));
  394. }
  395. if (has_any_capability(array( 'moodle/role:assign', 'moodle/role:safeoverride','moodle/role:override', 'moodle/role:review'), $coursecontext)) {
  396. // Override roles
  397. if (has_capability('moodle/role:review', $coursecontext)) {
  398. $url = new moodle_url('/admin/roles/permissions.php', array('contextid'=>$coursecontext->id));
  399. } else {
  400. $url = NULL;
  401. }
  402. $permissionsnode = $usersnode->add(get_string('permissions', 'role'), $url, navigation_node::TYPE_SETTING, null, 'override');
  403. // Add assign or override roles if allowed
  404. if ($course->id == SITEID or (!empty($CFG->adminsassignrolesincourse) and is_siteadmin())) {
  405. if (has_capability('moodle/role:assign', $coursecontext)) {
  406. $url = new moodle_url('/admin/roles/assign.php', array('contextid'=>$coursecontext->id));
  407. $permissionsnode->add(get_string('assignedroles', 'role'), $url, navigation_node::TYPE_SETTING, null, 'roles', new pix_icon('i/assignroles', ''));
  408. }
  409. }
  410. // Check role permissions
  411. if (has_any_capability(array('moodle/role:assign', 'moodle/role:safeoverride', 'moodle/role:override'), $coursecontext)) {
  412. $url = new moodle_url('/admin/roles/check.php', array('contextid'=>$coursecontext->id));
  413. $permissionsnode->add(get_string('checkpermissions', 'role'), $url, navigation_node::TYPE_SETTING, null, 'permissions', new pix_icon('i/checkpermissions', ''));
  414. }
  415. }
  416. // Deal somehow with users that are not enrolled but still got a role somehow
  417. if ($course->id != SITEID) {
  418. //TODO, create some new UI for role assignments at course level
  419. if (has_capability('moodle/course:reviewotherusers', $coursecontext)) {
  420. $url = new moodle_url('/enrol/otherusers.php', array('id'=>$course->id));
  421. $usersnode->add(get_string('notenrolledusers', 'enrol'), $url, navigation_node::TYPE_SETTING, null, 'otherusers', new pix_icon('i/assignroles', ''));
  422. }
  423. }
  424. // just in case nothing was actually added
  425. $usersnode->trim_if_empty();
  426. if ($course->id != SITEID) {
  427. if (isguestuser() or !isloggedin()) {
  428. // guest account can not be enrolled - no links for them
  429. } else if (is_enrolled($coursecontext)) {
  430. // unenrol link if possible
  431. foreach ($instances as $instance) {
  432. if (!isset($plugins[$instance->enrol])) {
  433. continue;
  434. }
  435. $plugin = $plugins[$instance->enrol];
  436. if ($unenrollink = $plugin->get_unenrolself_link($instance)) {
  437. $shortname = format_string($course->shortname, true, array('context' => $coursecontext));
  438. $coursenode->add(get_string('unenrolme', 'core_enrol', $shortname), $unenrollink, navigation_node::TYPE_SETTING, null, 'unenrolself', new pix_icon('i/user', ''));
  439. break;
  440. //TODO. deal with multiple unenrol links - not likely case, but still...
  441. }
  442. }
  443. } else {
  444. // enrol link if possible
  445. if (is_viewing($coursecontext)) {
  446. // better not show any enrol link, this is intended for managers and inspectors
  447. } else {
  448. foreach ($instances as $instance) {
  449. if (!isset($plugins[$instance->enrol])) {
  450. continue;
  451. }
  452. $plugin = $plugins[$instance->enrol];
  453. if ($plugin->show_enrolme_link($instance)) {
  454. $url = new moodle_url('/enrol/index.php', array('id'=>$course->id));
  455. $shortname = format_string($course->shortname, true, array('context' => $coursecontext));
  456. $coursenode->add(get_string('enrolme', 'core_enrol', $shortname), $url, navigation_node::TYPE_SETTING, null, 'enrolself', new pix_icon('i/user', ''));
  457. break;
  458. }
  459. }
  460. }
  461. }
  462. }
  463. }
  464. /**
  465. * Returns list of courses current $USER is enrolled in and can access
  466. *
  467. * The $fields param is a list of field names to ADD so name just the fields you really need,
  468. * which will be added and uniq'd.
  469. *
  470. * If $allaccessible is true, this will additionally return courses that the current user is not
  471. * enrolled in, but can access because they are open to the user for other reasons (course view
  472. * permission, currently viewing course as a guest, or course allows guest access without
  473. * password).
  474. *
  475. * @param string|array $fields Extra fields to be returned (array or comma-separated list).
  476. * @param string|null $sort Comma separated list of fields to sort by, defaults to respecting navsortmycoursessort.
  477. * Allowed prefixes for sort fields are: "ul" for the user_lastaccess table, "c" for the courses table,
  478. * "ue" for the user_enrolments table.
  479. * @param int $limit max number of courses
  480. * @param array $courseids the list of course ids to filter by
  481. * @param bool $allaccessible Include courses user is not enrolled in, but can access
  482. * @param int $offset Offset the result set by this number
  483. * @param array $excludecourses IDs of hidden courses to exclude from search
  484. * @return array
  485. */
  486. function enrol_get_my_courses($fields = null, $sort = null, $limit = 0, $courseids = [], $allaccessible = false,
  487. $offset = 0, $excludecourses = []) {
  488. global $DB, $USER, $CFG;
  489. // Re-Arrange the course sorting according to the admin settings.
  490. $sort = enrol_get_courses_sortingsql($sort);
  491. // Guest account does not have any enrolled courses.
  492. if (!$allaccessible && (isguestuser() or !isloggedin())) {
  493. return array();
  494. }
  495. $basefields = array('id', 'category', 'sortorder',
  496. 'shortname', 'fullname', 'idnumber',
  497. 'startdate', 'visible',
  498. 'groupmode', 'groupmodeforce', 'cacherev');
  499. if (empty($fields)) {
  500. $fields = $basefields;
  501. } else if (is_string($fields)) {
  502. // turn the fields from a string to an array
  503. $fields = explode(',', $fields);
  504. $fields = array_map('trim', $fields);
  505. $fields = array_unique(array_merge($basefields, $fields));
  506. } else if (is_array($fields)) {
  507. $fields = array_unique(array_merge($basefields, $fields));
  508. } else {
  509. throw new coding_exception('Invalid $fields parameter in enrol_get_my_courses()');
  510. }
  511. if (in_array('*', $fields)) {
  512. $fields = array('*');
  513. }
  514. $orderby = "";
  515. $sort = trim($sort);
  516. $sorttimeaccess = false;
  517. $allowedsortprefixes = array('c', 'ul', 'ue');
  518. if (!empty($sort)) {
  519. $rawsorts = explode(',', $sort);
  520. $sorts = array();
  521. foreach ($rawsorts as $rawsort) {
  522. $rawsort = trim($rawsort);
  523. if (preg_match('/^ul\.(\S*)\s(asc|desc)/i', $rawsort, $matches)) {
  524. if (strcasecmp($matches[2], 'asc') == 0) {
  525. $sorts[] = 'COALESCE(ul.' . $matches[1] . ', 0) ASC';
  526. } else {
  527. $sorts[] = 'COALESCE(ul.' . $matches[1] . ', 0) DESC';
  528. }
  529. $sorttimeaccess = true;
  530. } else if (strpos($rawsort, '.') !== false) {
  531. $prefix = explode('.', $rawsort);
  532. if (in_array($prefix[0], $allowedsortprefixes)) {
  533. $sorts[] = trim($rawsort);
  534. } else {
  535. throw new coding_exception('Invalid $sort parameter in enrol_get_my_courses()');
  536. }
  537. } else {
  538. $sorts[] = 'c.'.trim($rawsort);
  539. }
  540. }
  541. $sort = implode(',', $sorts);
  542. $orderby = "ORDER BY $sort";
  543. }
  544. $wheres = array("c.id <> :siteid");
  545. $params = array('siteid'=>SITEID);
  546. if (isset($USER->loginascontext) and $USER->loginascontext->contextlevel == CONTEXT_COURSE) {
  547. // list _only_ this course - anything else is asking for trouble...
  548. $wheres[] = "courseid = :loginas";
  549. $params['loginas'] = $USER->loginascontext->instanceid;
  550. }
  551. $coursefields = 'c.' .join(',c.', $fields);
  552. $ccselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
  553. $ccjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel)";
  554. $params['contextlevel'] = CONTEXT_COURSE;
  555. $wheres = implode(" AND ", $wheres);
  556. $timeaccessselect = "";
  557. $timeaccessjoin = "";
  558. if (!empty($courseids)) {
  559. list($courseidssql, $courseidsparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED);
  560. $wheres = sprintf("%s AND c.id %s", $wheres, $courseidssql);
  561. $params = array_merge($params, $courseidsparams);
  562. }
  563. if (!empty($excludecourses)) {
  564. list($courseidssql, $courseidsparams) = $DB->get_in_or_equal($excludecourses, SQL_PARAMS_NAMED, 'param', false);
  565. $wheres = sprintf("%s AND c.id %s", $wheres, $courseidssql);
  566. $params = array_merge($params, $courseidsparams);
  567. }
  568. $courseidsql = "";
  569. // Logged-in, non-guest users get their enrolled courses.
  570. if (!isguestuser() && isloggedin()) {
  571. $courseidsql .= "
  572. SELECT DISTINCT e.courseid
  573. FROM {enrol} e
  574. JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = :userid1)
  575. WHERE ue.status = :active AND e.status = :enabled AND ue.timestart < :now1
  576. AND (ue.timeend = 0 OR ue.timeend > :now2)";
  577. $params['userid1'] = $USER->id;
  578. $params['active'] = ENROL_USER_ACTIVE;
  579. $params['enabled'] = ENROL_INSTANCE_ENABLED;
  580. $params['now1'] = round(time(), -2); // Improves db caching.
  581. $params['now2'] = $params['now1'];
  582. if ($sorttimeaccess) {
  583. $params['userid2'] = $USER->id;
  584. $timeaccessselect = ', ul.timeaccess as lastaccessed';
  585. $timeaccessjoin = "LEFT JOIN {user_lastaccess} ul ON (ul.courseid = c.id AND ul.userid = :userid2)";
  586. }
  587. }
  588. // When including non-enrolled but accessible courses...
  589. if ($allaccessible) {
  590. if (is_siteadmin()) {
  591. // Site admins can access all courses.
  592. $courseidsql = "SELECT DISTINCT c2.id AS courseid FROM {course} c2";
  593. } else {
  594. // If we used the enrolment as well, then this will be UNIONed.
  595. if ($courseidsql) {
  596. $courseidsql .= " UNION ";
  597. }
  598. // Include courses with guest access and no password.
  599. $courseidsql .= "
  600. SELECT DISTINCT e.courseid
  601. FROM {enrol} e
  602. WHERE e.enrol = 'guest' AND e.password = :emptypass AND e.status = :enabled2";
  603. $params['emptypass'] = '';
  604. $params['enabled2'] = ENROL_INSTANCE_ENABLED;
  605. // Include courses where the current user is currently using guest access (may include
  606. // those which require a password).
  607. $courseids = [];
  608. $accessdata = get_user_accessdata($USER->id);
  609. foreach ($accessdata['ra'] as $contextpath => $roles) {
  610. if (array_key_exists($CFG->guestroleid, $roles)) {
  611. // Work out the course id from context path.
  612. $context = context::instance_by_id(preg_replace('~^.*/~', '', $contextpath));
  613. if ($context instanceof context_course) {
  614. $courseids[$context->instanceid] = true;
  615. }
  616. }
  617. }
  618. // Include courses where the current user has moodle/course:view capability.
  619. $courses = get_user_capability_course('moodle/course:view', null, false);
  620. if (!$courses) {
  621. $courses = [];
  622. }
  623. foreach ($courses as $course) {
  624. $courseids[$course->id] = true;
  625. }
  626. // If there are any in either category, list them individually.
  627. if ($courseids) {
  628. list ($allowedsql, $allowedparams) = $DB->get_in_or_equal(
  629. array_keys($courseids), SQL_PARAMS_NAMED);
  630. $courseidsql .= "
  631. UNION
  632. SELECT DISTINCT c3.id AS courseid
  633. FROM {course} c3
  634. WHERE c3.id $allowedsql";
  635. $params = array_merge($params, $allowedparams);
  636. }
  637. }
  638. }
  639. // Note: we can not use DISTINCT + text fields due to Oracle and MS limitations, that is why
  640. // we have the subselect there.
  641. $sql = "SELECT $coursefields $ccselect $timeaccessselect
  642. FROM {course} c
  643. JOIN ($courseidsql) en ON (en.courseid = c.id)
  644. $timeaccessjoin
  645. $ccjoin
  646. WHERE $wheres
  647. $orderby";
  648. $courses = $DB->get_records_sql($sql, $params, $offset, $limit);
  649. // preload contexts and check visibility
  650. foreach ($courses as $id=>$course) {
  651. context_helper::preload_from_record($course);
  652. if (!$course->visible) {
  653. if (!$context = context_course::instance($id, IGNORE_MISSING)) {
  654. unset($courses[$id]);
  655. continue;
  656. }
  657. if (!has_capability('moodle/course:viewhiddencourses', $context)) {
  658. unset($courses[$id]);
  659. continue;
  660. }
  661. }
  662. $courses[$id] = $course;
  663. }
  664. //wow! Is that really all? :-D
  665. return $courses;
  666. }
  667. /**
  668. * Returns course enrolment information icons.
  669. *
  670. * @param object $course
  671. * @param array $instances enrol instances of this course, improves performance
  672. * @return array of pix_icon
  673. */
  674. function enrol_get_course_info_icons($course, array $instances = NULL) {
  675. $icons = array();
  676. if (is_null($instances)) {
  677. $instances = enrol_get_instances($course->id, true);
  678. }
  679. $plugins = enrol_get_plugins(true);
  680. foreach ($plugins as $name => $plugin) {
  681. $pis = array();
  682. foreach ($instances as $instance) {
  683. if ($instance->status != ENROL_INSTANCE_ENABLED or $instance->courseid != $course->id) {
  684. debugging('Invalid instances parameter submitted in enrol_get_info_icons()');
  685. continue;
  686. }
  687. if ($instance->enrol == $name) {
  688. $pis[$instance->id] = $instance;
  689. }
  690. }
  691. if ($pis) {
  692. $icons = array_merge($icons, $plugin->get_info_icons($pis));
  693. }
  694. }
  695. return $icons;
  696. }
  697. /**
  698. * Returns SQL ORDER arguments which reflect the admin settings to sort my courses.
  699. *
  700. * @param string|null $sort SQL ORDER arguments which were originally requested (optionally).
  701. * @return string SQL ORDER arguments.
  702. */
  703. function enrol_get_courses_sortingsql($sort = null) {
  704. global $CFG;
  705. // Prepare the visible SQL fragment as empty.
  706. $visible = '';
  707. // Only create a visible SQL fragment if the caller didn't already pass a sort order which contains the visible field.
  708. if ($sort === null || strpos($sort, 'visible') === false) {
  709. // If the admin did not explicitly want to have shown and hidden courses sorted as one list, we will sort hidden
  710. // courses to the end of the course list.
  711. if (!isset($CFG->navsortmycourseshiddenlast) || $CFG->navsortmycourseshiddenlast == true) {
  712. $visible = 'visible DESC, ';
  713. }
  714. }
  715. // Only create a sortorder SQL fragment if the caller didn't already pass one.
  716. if ($sort === null) {
  717. // If the admin has configured a course sort order, we will use this.
  718. if (!empty($CFG->navsortmycoursessort)) {
  719. $sort = $CFG->navsortmycoursessort . ' ASC';
  720. // Otherwise we will fall back to the sortorder sorting.
  721. } else {
  722. $sort = 'sortorder ASC';
  723. }
  724. }
  725. return $visible . $sort;
  726. }
  727. /**
  728. * Returns course enrolment detailed information.
  729. *
  730. * @param object $course
  731. * @return array of html fragments - can be used to construct lists
  732. */
  733. function enrol_get_course_description_texts($course) {
  734. $lines = array();
  735. $instances = enrol_get_instances($course->id, true);
  736. $plugins = enrol_get_plugins(true);
  737. foreach ($instances as $instance) {
  738. if (!isset($plugins[$instance->enrol])) {
  739. //weird
  740. continue;
  741. }
  742. $plugin = $plugins[$instance->enrol];
  743. $text = $plugin->get_description_text($instance);
  744. if ($text !== NULL) {
  745. $lines[] = $text;
  746. }
  747. }
  748. return $lines;
  749. }
  750. /**
  751. * Returns list of courses user is enrolled into.
  752. *
  753. * Note: Use {@link enrol_get_all_users_courses()} if you need the list without any capability checks.
  754. *
  755. * The $fields param is a list of field names to ADD so name just the fields you really need,
  756. * which will be added and uniq'd.
  757. *
  758. * @param int $userid User whose courses are returned, defaults to the current user.
  759. * @param bool $onlyactive Return only active enrolments in courses user may see.
  760. * @param string|array $fields Extra fields to be returned (array or comma-separated list).
  761. * @param string|null $sort Comma separated list of fields to sort by, defaults to respecting navsortmycoursessort.
  762. * @return array
  763. */
  764. function enrol_get_users_courses($userid, $onlyactive = false, $fields = null, $sort = null) {
  765. global $DB;
  766. $courses = enrol_get_all_users_courses($userid, $onlyactive, $fields, $sort);
  767. // preload contexts and check visibility
  768. if ($onlyactive) {
  769. foreach ($courses as $id=>$course) {
  770. context_helper::preload_from_record($course);
  771. if (!$course->visible) {
  772. if (!$context = context_course::instance($id)) {
  773. unset($courses[$id]);
  774. continue;
  775. }
  776. if (!has_capability('moodle/course:viewhiddencourses', $context, $userid)) {
  777. unset($courses[$id]);
  778. continue;
  779. }
  780. }
  781. }
  782. }
  783. return $courses;
  784. }
  785. /**
  786. * Returns list of roles per users into course.
  787. *
  788. * @param int $courseid Course id.
  789. * @return array Array[$userid][$roleid] = role_assignment.
  790. */
  791. function enrol_get_course_users_roles(int $courseid) : array {
  792. global $DB;
  793. $context = context_course::instance($courseid);
  794. $roles = array();
  795. $records = $DB->get_recordset('role_assignments', array('contextid' => $context->id));
  796. foreach ($records as $record) {
  797. if (isset($roles[$record->userid]) === false) {
  798. $roles[$record->userid] = array();
  799. }
  800. $roles[$record->userid][$record->roleid] = $record;
  801. }
  802. $records->close();
  803. return $roles;
  804. }
  805. /**
  806. * Can user access at least one enrolled course?
  807. *
  808. * Cheat if necessary, but find out as fast as possible!
  809. *
  810. * @param int|stdClass $user null means use current user
  811. * @return bool
  812. */
  813. function enrol_user_sees_own_courses($user = null) {
  814. global $USER;
  815. if ($user === null) {
  816. $user = $USER;
  817. }
  818. $userid = is_object($user) ? $user->id : $user;
  819. // Guest account does not have any courses
  820. if (isguestuser($userid) or empty($userid)) {
  821. return false;
  822. }
  823. // Let's cheat here if this is the current user,
  824. // if user accessed any course recently, then most probably
  825. // we do not need to query the database at all.
  826. if ($USER->id == $userid) {
  827. if (!empty($USER->enrol['enrolled'])) {
  828. foreach ($USER->enrol['enrolled'] as $until) {
  829. if ($until > time()) {
  830. return true;
  831. }
  832. }
  833. }
  834. }
  835. // Now the slow way.
  836. $courses = enrol_get_all_users_courses($userid, true);
  837. foreach($courses as $course) {
  838. if ($course->visible) {
  839. return true;
  840. }
  841. context_helper::preload_from_record($course);
  842. $context = context_course::instance($course->id);
  843. if (has_capability('moodle/course:viewhiddencourses', $context, $user)) {
  844. return true;
  845. }
  846. }
  847. return false;
  848. }
  849. /**
  850. * Returns list of courses user is enrolled into without performing any capability checks.
  851. *
  852. * The $fields param is a list of field names to ADD so name just the fields you really need,
  853. * which will be added and uniq'd.
  854. *
  855. * @param int $userid User whose courses are returned, defaults to the current user.
  856. * @param bool $onlyactive Return only active enrolments in courses user may see.
  857. * @param string|array $fields Extra fields to be returned (array or comma-separated list).
  858. * @param string|null $sort Comma separated list of fields to sort by, defaults to respecting navsortmycoursessort.
  859. * @return array
  860. */
  861. function enrol_get_all_users_courses($userid, $onlyactive = false, $fields = null, $sort = null) {
  862. global $DB;
  863. // Re-Arrange the course sorting according to the admin settings.
  864. $sort = enrol_get_courses_sortingsql($sort);
  865. // Guest account does not have any courses
  866. if (isguestuser($userid) or empty($userid)) {
  867. return(array());
  868. }
  869. $basefields = array('id', 'category', 'sortorder',
  870. 'shortname', 'fullname', 'idnumber',
  871. 'startdate', 'visible',
  872. 'defaultgroupingid',
  873. 'groupmode', 'groupmodeforce');
  874. if (empty($fields)) {
  875. $fields = $basefields;
  876. } else if (is_string($fields)) {
  877. // turn the fields from a string to an array
  878. $fields = explode(',', $fields);
  879. $fields = array_map('trim', $fields);
  880. $fields = array_unique(array_merge($basefields, $fields));
  881. } else if (is_array($fields)) {
  882. $fields = array_unique(array_merge($basefields, $fields));
  883. } else {
  884. throw new coding_exception('Invalid $fields parameter in enrol_get_all_users_courses()');
  885. }
  886. if (in_array('*', $fields)) {
  887. $fields = array('*');
  888. }
  889. $orderby = "";
  890. $sort = trim($sort);
  891. if (!empty($sort)) {
  892. $rawsorts = explode(',', $sort);
  893. $sorts = array();
  894. foreach ($rawsorts as $rawsort) {
  895. $rawsort = trim($rawsort);
  896. if (strpos($rawsort, 'c.') === 0) {
  897. $rawsort = substr($rawsort, 2);
  898. }
  899. $sorts[] = trim($rawsort);
  900. }
  901. $sort = 'c.'.implode(',c.', $sorts);
  902. $orderby = "ORDER BY $sort";
  903. }
  904. $params = array('siteid'=>SITEID);
  905. if ($onlyactive) {
  906. $subwhere = "WHERE ue.status = :active AND e.status = :enabled AND ue.timestart < :now1 AND (ue.timeend = 0 OR ue.timeend > :now2)";
  907. $params['now1'] = round(time(), -2); // improves db caching
  908. $params['now2'] = $params['now1'];
  909. $params['active'] = ENROL_USER_ACTIVE;
  910. $params['enabled'] = ENROL_INSTANCE_ENABLED;
  911. } else {
  912. $subwhere = "";
  913. }
  914. $coursefields = 'c.' .join(',c.', $fields);
  915. $ccselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
  916. $ccjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel)";
  917. $params['contextlevel'] = CONTEXT_COURSE;
  918. //note: we can not use DISTINCT + text fields due to Oracle and MS limitations, that is why we have the subselect there
  919. $sql = "SELECT $coursefields $ccselect
  920. FROM {course} c
  921. JOIN (SELECT DISTINCT e.courseid
  922. FROM {enrol} e
  923. JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = :userid)
  924. $subwhere
  925. ) en ON (en.courseid = c.id)
  926. $ccjoin
  927. WHERE c.id <> :siteid
  928. $orderby";
  929. $params['userid'] = $userid;
  930. $courses = $DB->get_records_sql($sql, $params);
  931. return $courses;
  932. }
  933. /**
  934. * Called when user is about to be deleted.
  935. * @param object $user
  936. * @return void
  937. */
  938. function enrol_user_delete($user) {
  939. global $DB;
  940. $plugins = enrol_get_plugins(true);
  941. foreach ($plugins as $plugin) {
  942. $plugin->user_delete($user);
  943. }
  944. // force cleanup of all broken enrolments
  945. $DB->delete_records('user_enrolments', array('userid'=>$user->id));
  946. }
  947. /**
  948. * Called when course is about to be deleted.
  949. * @param stdClass $course
  950. * @return void
  951. */
  952. function enrol_course_delete($course) {
  953. global $DB;
  954. $instances = enrol_get_instances($course->id, false);
  955. $plugins = enrol_get_plugins(true);
  956. foreach ($instances as $instance) {
  957. if (isset($plugins[$instance->enrol])) {
  958. $plugins[$instance->enrol]->delete_instance($instance);
  959. }
  960. // low level delete in case plugin did not do it
  961. $DB->delete_records('user_enrolments', array('enrolid'=>$instance->id));
  962. $DB->delete_records('role_assignments', array('itemid'=>$instance->id, 'component'=>'enrol_'.$instance->enrol));
  963. $DB->delete_records('user_enrolments', array('enrolid'=>$instance->id));
  964. $DB->delete_records('enrol', array('id'=>$instance->id));
  965. }
  966. }
  967. /**
  968. * Try to enrol user via default internal auth plugin.
  969. *
  970. * For now this is always using the manual enrol plugin...
  971. *
  972. * @param $courseid
  973. * @param $userid
  974. * @param $roleid
  975. * @param $timestart
  976. * @param $timeend
  977. * @return bool success
  978. */
  979. function enrol_try_internal_enrol($courseid, $userid, $roleid = null, $timestart = 0, $timeend = 0) {
  980. global $DB;
  981. //note: this is hardcoded to manual plugin for now
  982. if (!enrol_is_enabled('manual')) {
  983. return false;
  984. }
  985. if (!$enrol = enrol_get_plugin('manual')) {
  986. return false;
  987. }
  988. if (!$instances = $DB->get_records('enrol', array('enrol'=>'manual', 'courseid'=>$courseid, 'status'=>ENROL_INSTANCE_ENABLED), 'sortorder,id ASC')) {
  989. return false;
  990. }
  991. $instance = reset($instances);
  992. $enrol->enrol_user($instance, $userid, $roleid, $timestart, $timeend);
  993. return true;
  994. }
  995. /**
  996. * Is there a chance users might self enrol
  997. * @param int $courseid
  998. * @return bool
  999. */
  1000. function enrol_selfenrol_available($courseid) {
  1001. $result = false;
  1002. $plugins = enrol_get_plugins(true);
  1003. $enrolinstances = enrol_get_instances($courseid, true);
  1004. foreach($enrolinstances as $instance) {
  1005. if (!isset($plugins[$instance->enrol])) {
  1006. continue;
  1007. }
  1008. if ($instance->enrol === 'guest') {
  1009. // blacklist known temporary guest plugins
  1010. continue;
  1011. }
  1012. if ($plugins[$instance->enrol]->show_enrolme_link($instance)) {
  1013. $result = true;
  1014. break;
  1015. }
  1016. }
  1017. return $result;
  1018. }
  1019. /**
  1020. * This function returns the end of current active user enrolment.
  1021. *
  1022. * It deals correctly with multiple overlapping user enrolments.
  1023. *
  1024. * @param int $courseid
  1025. * @param int $userid
  1026. * @return int|bool timestamp when active enrolment ends, false means no active enrolment now, 0 means never
  1027. */
  1028. function enrol_get_enrolment_end($courseid, $userid) {
  1029. global $DB;
  1030. $sql = "SELECT ue.*
  1031. FROM {user_enrolments} ue
  1032. JOIN {enrol} e ON (e.id = ue.enrolid AND e.courseid = :courseid)
  1033. JOIN {user} u ON u.id = ue.userid
  1034. WHERE ue.userid = :userid AND ue.status = :active AND e.status = :enabled AND u.deleted = 0";
  1035. $params = array('enabled'=>ENROL_INSTANCE_ENABLED, 'active'=>ENROL_USER_ACTIVE, 'userid'=>$userid, 'courseid'=>$courseid);
  1036. if (!$enrolments = $DB->get_records_sql($sql, $params)) {
  1037. return false;
  1038. }
  1039. $changes = array();
  1040. foreach ($enrolments as $ue) {
  1041. $start = (int)$ue->timestart;
  1042. $end = (int)$ue->timeend;
  1043. if ($end != 0 and $end < $start) {
  1044. debugging('Invalid enrolment start or end in user_enrolment id:'.$ue->id);
  1045. continue;
  1046. }
  1047. if (isset($changes[$start])) {
  1048. $changes[$start] = $changes[$start] + 1;
  1049. } else {
  1050. $changes[$start] = 1;
  1051. }
  1052. if ($end === 0) {
  1053. // no end
  1054. } else if (isset($changes[$end])) {
  1055. $changes[$end] = $changes[$end] - 1;
  1056. } else {
  1057. $changes[$end] = -1;
  1058. }
  1059. }
  1060. // let's sort then enrolment starts&ends and go through them chronologically,
  1061. // looking for current status and the next future end of enrolment
  1062. ksort($changes);
  1063. $now = time();
  1064. $current = 0;
  1065. $present = null;
  1066. foreach ($changes as $time => $change) {
  1067. if ($time > $now) {
  1068. if ($present === null) {
  1069. // we have just went past current time
  1070. $present = $current;
  1071. if ($present < 1) {
  1072. // no enrolment active
  1073. return false;
  1074. }
  1075. }
  1076. if ($present !== null) {
  1077. // we are already in the future - look for possible end
  1078. if ($current + $change < 1) {
  1079. return $time;
  1080. }
  1081. }
  1082. }
  1083. $current += $change;
  1084. }
  1085. if ($current > 0) {
  1086. return 0;
  1087. } else {
  1088. return false;
  1089. }
  1090. }
  1091. /**
  1092. * Is current user accessing course via this enrolment method?
  1093. *
  1094. * This is intended for operations that are going to affect enrol instances.
  1095. *
  1096. * @param stdClass $instance enrol instance
  1097. * @return bool
  1098. */
  1099. function enrol_accessing_via_instance(stdClass $instance) {
  1100. global $DB, $USER;
  1101. if (empty($instance->id)) {
  1102. return false;
  1103. }
  1104. if (is_siteadmin()) {
  1105. // Admins may go anywhere.
  1106. return false;
  1107. }
  1108. return $DB->record_exists('user_enrolments', array('userid'=>$USER->id, 'enrolid'=>$instance->id));
  1109. }
  1110. /**
  1111. * Returns true if user is enrolled (is participating) in course
  1112. * this is intended for students and teachers.
  1113. *
  1114. * Since 2.2 the result for active enrolments and current user are cached.
  1115. *
  1116. * @param context $context
  1117. * @param int|stdClass $user if null $USER is used, otherwise user object or id expected
  1118. * @param string $withcapability extra capability name
  1119. * @param bool $onlyactive consider only active enrolments in enabled plugins and time restrictions
  1120. * @return bool
  1121. */
  1122. function is_enrolled(context $context, $user = null, $withcapability = '', $onlyactive = false) {
  1123. global $USER, $DB;
  1124. // First find the course context.
  1125. $coursecontext = $context->get_course_context();
  1126. // Make sure there is a real user specified.
  1127. if ($user === null) {
  1128. $userid = isset($USER->id) ? $USER->id : 0;
  1129. } else {
  1130. $userid = is_object($user) ? $user->id : $user;
  1131. }
  1132. if (empty($userid)) {
  1133. // Not-logged-in!
  1134. return false;
  1135. } else if (isguestuser($userid)) {
  1136. // Guest account can not be enrolled anywhere.
  1137. return false;
  1138. }
  1139. // Note everybody participates on frontpage, so for other contexts...
  1140. if ($coursecontext->instanceid != SITEID) {
  1141. // Try cached info first - the enrolled flag is set only when active enrolment present.
  1142. if ($USER->id == $userid) {
  1143. $coursecontext->reload_if_dirty();
  1144. if (isset($USER->enrol['enrolled'][$coursecontext->instanceid])) {
  1145. if ($USER->enrol['enrolled'][$coursecontext->instanceid] > time()) {
  1146. if ($withcapability and !has_capability($withcapability, $context, $userid)) {
  1147. return false;
  1148. }
  1149. return true;
  1150. }
  1151. }
  1152. }
  1153. if ($onlyactive) {
  1154. // Look for active enrolments only.
  1155. $until = enrol_get_enrolment_end($coursecontext->instanceid, $userid);
  1156. if ($until === false) {
  1157. return false;
  1158. }
  1159. if ($USER->id == $userid) {
  1160. if ($until == 0) {
  1161. $until = ENROL_MAX_TIMESTAMP;
  1162. }
  1163. $USER->enrol['enrolled'][$coursecontext->instanceid] = $until;
  1164. if (isset($USER->enrol['tempguest'][$coursecontext->instanceid])) {
  1165. unset($USER->enrol['tempguest'][$coursecontext->instanceid]);
  1166. remove_temp_course_roles($coursecontext);
  1167. }
  1168. }
  1169. } else {
  1170. // Any enrolment is good for us here, even outdated, disabled or inactive.
  1171. $sql = "SELECT 'x'
  1172. FROM {user_enrolments} ue
  1173. JOIN {enrol} e ON (e.id = ue.enrolid AND e.courseid = :courseid)
  1174. JOIN {user} u ON u.id = ue.userid
  1175. WHERE ue.userid = :userid AND u.deleted = 0";
  1176. $params = array('userid' => $userid, 'courseid' => $coursecontext->instanceid);
  1177. if (!$DB->record_exists_sql($sql, $params)) {
  1178. return false;
  1179. }
  1180. }
  1181. }
  1182. if ($withcapability and !has_capability($withcapability, $context, $userid)) {
  1183. return false;
  1184. }
  1185. return true;
  1186. }
  1187. /**
  1188. * Returns an array of joins, wheres and params that will limit the group of
  1189. * users to only those enrolled and with given capability (if specified).
  1190. *
  1191. * Note this join will return duplicate rows for users who have been enrolled
  1192. * several times (e.g. as manual enrolment, and as self enrolment). You may
  1193. * need to use a SELECT DISTINCT in your query (see get_enrolled_sql for example).
  1194. *
  1195. * @param context $context
  1196. * @param string $prefix optional, a prefix to the user id column
  1197. * @param string|array $capability optional, may include a capability name, or array of names.
  1198. * If an array is provided then this is the equivalent of a logical 'OR',
  1199. * i.e. the user needs to have one of these capabilities.
  1200. * @param int $group optional, 0 indicates no current group and USERSWITHOUTGROUP users without any group; otherwise the group id
  1201. * @param bool $onlyactive consider only active enrolments in enabled plugins and time restrictions
  1202. * @param bool $onlysuspended inverse of onlyactive, consider only suspended enrolments
  1203. * @param int $enrolid The enrolment ID. If not 0, only users enrolled using this enrolment method will be returned.
  1204. * @return \core\dml\sql_join Contains joins, wheres, params
  1205. */
  1206. function get_enrolled_with_capabilities_join(context $context, $prefix = '', $capability = '', $group = 0,
  1207. $onlyactive = false, $onlysuspended = false, $enrolid = 0) {
  1208. $uid = $prefix . 'u.id';
  1209. $joins = array();
  1210. $wheres = array();
  1211. $enrolledjoin = get_enrolled_join($context, $uid, $onlyactive, $onlysuspended, $enrolid);
  1212. $joins[] = $enrolledjoin->joins;
  1213. $wheres[] = $enrolledjoin->wheres;
  1214. $params = $enrolledjoin->params;
  1215. if (!empty($capability)) {
  1216. $capjoin = get_with_capability_join($context, $capability, $uid);
  1217. $joins[] = $capjoin->joins;
  1218. $wheres[] = $capjoin->wheres;
  1219. $params = array_merge($params, $capjoin->params);
  1220. }

Large files files are truncated, but you can click here to view the full file