PageRenderTime 433ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/lib.php

https://github.com/roelmann/krystle
PHP | 903 lines | 660 code | 79 blank | 164 comment | 207 complexity | c884a30a17f4cdd61127c077e645227e MD5 | raw file
  1. <?php
  2. /**
  3. * get_performance_output() override get_peformance_info()
  4. * in moodlelib.php. Returns a string
  5. * values ready for use.
  6. *
  7. * @return string
  8. */
  9. function krystle_performance_output($param) {
  10. $html = '<div class="performanceinfo"><ul>';
  11. if (isset($param['realtime'])) $html .= '<li><a class="red" href="#"><var>'.$param['realtime'].' secs</var><span>Load Time</span></a></li>';
  12. if (isset($param['memory_total'])) $html .= '<li><a class="orange" href="#"><var>'.display_size($param['memory_total']).'</var><span>Memory Used</span></a></li>';
  13. if (isset($param['includecount'])) $html .= '<li><a class="blue" href="#"><var>'.$param['includecount'].' Files </var><span>Included</span></a></li>';
  14. if (isset($param['dbqueries'])) $html .= '<li><a class="purple" href="#"><var>'.$param['dbqueries'].' </var><span>DB Read/Write</span></a></li>';
  15. $html .= '</ul></div>';
  16. return $html;
  17. }
  18. /**
  19. * Makes our changes to the CSS
  20. *
  21. * @param string $css
  22. * @param theme_config $theme
  23. * @return string
  24. */
  25. function krystle_process_css($css, $theme) {
  26. if (!empty($theme->settings->backgroundcolor)) {
  27. $backgroundcolor = $theme->settings->backgroundcolor;
  28. } else {
  29. $backgroundcolor = null;
  30. }
  31. $css = krystle_set_backgroundcolor($css, $backgroundcolor);
  32. if (!empty($theme->settings->logourl)) {
  33. $logourl = $theme->settings->logourl;
  34. } else {
  35. $logourl = null;
  36. }
  37. $css = krystle_set_logourl($css, $logourl);
  38. if (!empty($theme->settings->menubackgroundcolor)) {
  39. $menubackgroundcolor = $theme->settings->menubackgroundcolor;
  40. } else {
  41. $menubackgroundcolor = null;
  42. }
  43. $css = krystle_set_menubackgroundcolor($css, $menubackgroundcolor);
  44. if (!empty($theme->settings->customcss)) {
  45. $customcss = $theme->settings->customcss;
  46. } else {
  47. $customcss = null;
  48. }
  49. $css = krystle_set_customcss($css, $customcss);
  50. return $css;
  51. }
  52. /**
  53. * Sets the background colour variable in CSS
  54. *
  55. * @param string $css
  56. * @param mixed $backgroundcolor
  57. * @return string
  58. */
  59. function krystle_set_backgroundcolor($css, $backgroundcolor) {
  60. $tag = '[[setting:backgroundcolor]]';
  61. $replacement = $backgroundcolor;
  62. if (is_null($replacement)) {
  63. $replacement = '#000066';
  64. }
  65. $css = str_replace($tag, $replacement, $css);
  66. return $css;
  67. }
  68. function krystle_set_logourl($css, $logourl) {
  69. global $OUTPUT;
  70. $tag = '[[setting:logourl]]';
  71. if (is_null($logourl)) {
  72. $replacement = $OUTPUT->pix_url('logo', 'theme'); //Default image
  73. }
  74. else {
  75. $protocol = '://';
  76. $ntp = strpos($logourl, $protocol); // Check to see if a networking protocol is used
  77. if($ntp === false) { // No networking protocol used
  78. $relative = '/';
  79. $rel = strpos($logourl, $relative); // Check to see if a relative path is used
  80. if($rel !== 0) { // Doesn't start with a slash
  81. $replacement = $OUTPUT->pix_url("$logourl", 'theme'); // Using Moodle output
  82. } else {
  83. $replacement = $logourl;
  84. }
  85. } else {
  86. $replacement = $logourl;
  87. }
  88. }
  89. $css = str_replace($tag, $replacement, $css);
  90. return $css;
  91. }
  92. /**
  93. * Sets the menu background colour variable in CSS
  94. *
  95. * @param string $css
  96. * @param mixed $menubackgroundcolor
  97. * @return string
  98. */
  99. function krystle_set_menubackgroundcolor($css, $menubackgroundcolor) {
  100. $tag = '[[setting:menubackgroundcolor]]';
  101. $replacement = $menubackgroundcolor;
  102. if (is_null($replacement)) {
  103. $replacement = '#000044';
  104. }
  105. $css = str_replace($tag, $replacement, $css);
  106. return $css;
  107. }
  108. /**
  109. * Sets the custom css variable in CSS
  110. *
  111. * @param string $css
  112. * @param mixed $customcss
  113. * @return string
  114. */
  115. function krystle_set_customcss($css, $customcss) {
  116. $tag = '[[setting:customcss]]';
  117. $replacement = $customcss;
  118. if (is_null($replacement)) {
  119. $replacement = '';
  120. }
  121. $css = str_replace($tag, $replacement, $css);
  122. return $css;
  123. }
  124. /**
  125. * Adds the JavaScript for the edit buttons to the page.
  126. *
  127. * The edit buttoniser is a YUI moodle module that is located in
  128. * theme/krystle/yui/editbuttons/editbuttons.js
  129. *
  130. * @param moodle_page $page
  131. */
  132. function krystle_initialise_editbuttons(moodle_page $page) {
  133. $page->requires->string_for_js('edit', 'moodle');
  134. $page->requires->yui_module('moodle-theme_krystle-editbuttons', 'M.theme_krystle.initEditButtons');
  135. }
  136. function krystle_initialise_awesomebar(moodle_page $page) {
  137. // Ensure that navigation has been initialised properly, in case Navigation block is not visible in 2.4
  138. $page->navigation->initialise();
  139. $page->requires->yui_module('moodle-theme_krystle-awesomebar', 'M.theme_krystle.initAwesomeBar');
  140. }
  141. function krystle_require_course_login($courseorid, $autologinguest = true, $cm = NULL, $setwantsurltome = true, $preventredirect = true) {
  142. global $CFG, $SITE;
  143. $issite = (is_object($courseorid) and $courseorid->id == SITEID)
  144. or (!is_object($courseorid) and $courseorid == SITEID);
  145. if ($issite && !empty($cm) && !($cm instanceof cm_info)) {
  146. // note: nearly all pages call get_fast_modinfo anyway and it does not make any
  147. // db queries so this is not really a performance concern, however it is obviously
  148. // better if you use get_fast_modinfo to get the cm before calling this.
  149. if (is_object($courseorid)) {
  150. $course = $courseorid;
  151. } else {
  152. $course = clone($SITE);
  153. }
  154. $modinfo = get_fast_modinfo($course);
  155. $cm = $modinfo->get_cm($cm->id);
  156. }
  157. if (!empty($CFG->forcelogin)) {
  158. // login required for both SITE and courses
  159. krystle_require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
  160. } else if ($issite && !empty($cm) and !$cm->uservisible) {
  161. // always login for hidden activities
  162. krystle_require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
  163. } else if ($issite) {
  164. //login for SITE not required
  165. if ($cm and empty($cm->visible)) {
  166. // hidden activities are not accessible without login
  167. krystle_require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
  168. } else if ($cm and !empty($CFG->enablegroupmembersonly) and $cm->groupmembersonly) {
  169. // not-logged-in users do not have any group membership
  170. krystle_require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
  171. } else {
  172. // We still need to instatiate PAGE vars properly so that things
  173. // that rely on it like navigation function correctly.
  174. if (!empty($courseorid)) {
  175. if (is_object($courseorid)) {
  176. $course = $courseorid;
  177. } else {
  178. $course = clone($SITE);
  179. }
  180. if ($cm) {
  181. if ($cm->course != $course->id) {
  182. throw new coding_exception('course and cm parameters in require_course_login() call do not match!!');
  183. }
  184. }
  185. }
  186. return;
  187. }
  188. } else {
  189. // course login always required
  190. krystle_require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
  191. }
  192. }
  193. function krystle_require_login($courseorid = NULL, $autologinguest = true, $cm = NULL, $setwantsurltome = true, $preventredirect = false) {
  194. global $CFG, $SESSION, $USER, $FULLME, $DB;
  195. // setup global $COURSE, themes, language and locale
  196. if (!empty($courseorid)) {
  197. if (is_object($courseorid)) {
  198. $course = $courseorid;
  199. } else if ($courseorid == SITEID) {
  200. $course = clone($SITE);
  201. } else {
  202. $course = $DB->get_record('course', array('id' => $courseorid), '*', MUST_EXIST);
  203. }
  204. if ($cm) {
  205. if ($cm->course != $course->id) {
  206. throw new coding_exception('course and cm parameters in require_login() call do not match!!');
  207. }
  208. // make sure we have a $cm from get_fast_modinfo as this contains activity access details
  209. if (!($cm instanceof cm_info)) {
  210. // note: nearly all pages call get_fast_modinfo anyway and it does not make any
  211. // db queries so this is not really a performance concern, however it is obviously
  212. // better if you use get_fast_modinfo to get the cm before calling this.
  213. $modinfo = get_fast_modinfo($course);
  214. $cm = $modinfo->get_cm($cm->id);
  215. }
  216. }
  217. } else {
  218. // do not touch global $COURSE via $PAGE->set_course(),
  219. // the reasons is we need to be able to call require_login() at any time!!
  220. $course = $SITE;
  221. if ($cm) {
  222. throw new coding_exception('cm parameter in require_login() requires valid course parameter!');
  223. }
  224. }
  225. // If the user is not even logged in yet then make sure they are
  226. if (!isloggedin()) {
  227. if ($autologinguest and !empty($CFG->guestloginbutton) and !empty($CFG->autologinguests)) {
  228. if (!$guest = get_complete_user_data('id', $CFG->siteguest)) {
  229. // misconfigured site guest, just redirect to login page
  230. redirect(get_login_url());
  231. exit; // never reached
  232. }
  233. $lang = isset($SESSION->lang) ? $SESSION->lang : $CFG->lang;
  234. complete_user_login($guest);
  235. $USER->autologinguest = true;
  236. $SESSION->lang = $lang;
  237. } else {
  238. //NOTE: $USER->site check was obsoleted by session test cookie,
  239. // $USER->confirmed test is in login/index.php
  240. if ($preventredirect) {
  241. throw new require_login_exception('You are not logged in');
  242. }
  243. if ($setwantsurltome) {
  244. // TODO: switch to PAGE->url
  245. $SESSION->wantsurl = $FULLME;
  246. }
  247. if (!empty($_SERVER['HTTP_REFERER'])) {
  248. $SESSION->fromurl = $_SERVER['HTTP_REFERER'];
  249. }
  250. redirect(get_login_url());
  251. exit; // never reached
  252. }
  253. }
  254. // loginas as redirection if needed
  255. if ($course->id != SITEID and session_is_loggedinas()) {
  256. if ($USER->loginascontext->contextlevel == CONTEXT_COURSE) {
  257. if ($USER->loginascontext->instanceid != $course->id) {
  258. print_error('loginasonecourse', '', $CFG->wwwroot.'/course/view.php?id='.$USER->loginascontext->instanceid);
  259. }
  260. }
  261. }
  262. // check whether the user should be changing password (but only if it is REALLY them)
  263. if (get_user_preferences('auth_forcepasswordchange') && !session_is_loggedinas()) {
  264. $userauth = get_auth_plugin($USER->auth);
  265. if ($userauth->can_change_password() and !$preventredirect) {
  266. $SESSION->wantsurl = $FULLME;
  267. if ($changeurl = $userauth->change_password_url()) {
  268. //use plugin custom url
  269. redirect($changeurl);
  270. } else {
  271. //use moodle internal method
  272. if (empty($CFG->loginhttps)) {
  273. redirect($CFG->wwwroot .'/login/change_password.php');
  274. } else {
  275. $wwwroot = str_replace('http:','https:', $CFG->wwwroot);
  276. redirect($wwwroot .'/login/change_password.php');
  277. }
  278. }
  279. } else {
  280. print_error('nopasswordchangeforced', 'auth');
  281. }
  282. }
  283. // Check that the user account is properly set up
  284. if (user_not_fully_set_up($USER)) {
  285. if ($preventredirect) {
  286. throw new require_login_exception('User not fully set-up');
  287. }
  288. $SESSION->wantsurl = $FULLME;
  289. redirect($CFG->wwwroot .'/user/edit.php?id='. $USER->id .'&amp;course='. SITEID);
  290. }
  291. // Make sure the USER has a sesskey set up. Used for CSRF protection.
  292. sesskey();
  293. // Do not bother admins with any formalities
  294. if (is_siteadmin()) {
  295. return;
  296. }
  297. // Fetch the system context, the course context, and prefetch its child contexts
  298. $sysctx = get_context_instance(CONTEXT_SYSTEM);
  299. $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id, MUST_EXIST);
  300. if ($cm) {
  301. $cmcontext = get_context_instance(CONTEXT_MODULE, $cm->id, MUST_EXIST);
  302. } else {
  303. $cmcontext = null;
  304. }
  305. // make sure the course itself is not hidden
  306. if ($course->id == SITEID) {
  307. // frontpage can not be hidden
  308. } else {
  309. if (is_role_switched($course->id)) {
  310. // when switching roles ignore the hidden flag - user had to be in course to do the switch
  311. } else {
  312. if (!$course->visible and !has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
  313. // originally there was also test of parent category visibility,
  314. // BUT is was very slow in complex queries involving "my courses"
  315. // now it is also possible to simply hide all courses user is not enrolled in :-)
  316. if ($preventredirect) {
  317. throw new require_login_exception('Course is hidden');
  318. }
  319. notice(get_string('coursehidden'), $CFG->wwwroot .'/');
  320. }
  321. }
  322. }
  323. // is the user enrolled?
  324. if ($course->id == SITEID) {
  325. // everybody is enrolled on the frontpage
  326. } else {
  327. if (session_is_loggedinas()) {
  328. // Make sure the REAL person can access this course first
  329. $realuser = session_get_realuser();
  330. if (!is_enrolled($coursecontext, $realuser->id, '', true) and !is_viewing($coursecontext, $realuser->id) and !is_siteadmin($realuser->id)) {
  331. if ($preventredirect) {
  332. throw new require_login_exception('Invalid course login-as access');
  333. }
  334. echo $OUTPUT->header();
  335. notice(get_string('studentnotallowed', '', fullname($USER, true)), $CFG->wwwroot .'/');
  336. }
  337. }
  338. // very simple enrolment caching - changes in course setting are not reflected immediately
  339. if (!isset($USER->enrol)) {
  340. $USER->enrol = array();
  341. $USER->enrol['enrolled'] = array();
  342. $USER->enrol['tempguest'] = array();
  343. }
  344. $access = false;
  345. if (is_viewing($coursecontext, $USER)) {
  346. // ok, no need to mess with enrol
  347. $access = true;
  348. } else {
  349. if (isset($USER->enrol['enrolled'][$course->id])) {
  350. if ($USER->enrol['enrolled'][$course->id] == 0) {
  351. $access = true;
  352. } else if ($USER->enrol['enrolled'][$course->id] > time()) {
  353. $access = true;
  354. } else {
  355. //expired
  356. unset($USER->enrol['enrolled'][$course->id]);
  357. }
  358. }
  359. if (isset($USER->enrol['tempguest'][$course->id])) {
  360. if ($USER->enrol['tempguest'][$course->id] == 0) {
  361. $access = true;
  362. } else if ($USER->enrol['tempguest'][$course->id] > time()) {
  363. $access = true;
  364. } else {
  365. //expired
  366. unset($USER->enrol['tempguest'][$course->id]);
  367. $USER->access = remove_temp_roles($coursecontext, $USER->access);
  368. }
  369. }
  370. if ($access) {
  371. // cache ok
  372. } else if (is_enrolled($coursecontext, $USER, '', true)) {
  373. // active participants may always access
  374. // TODO: refactor this into some new function
  375. $now = time();
  376. $sql = "SELECT MAX(ue.timeend)
  377. FROM {user_enrolments} ue
  378. JOIN {enrol} e ON (e.id = ue.enrolid AND e.courseid = :courseid)
  379. JOIN {user} u ON u.id = ue.userid
  380. WHERE ue.userid = :userid AND ue.status = :active AND e.status = :enabled AND u.deleted = 0
  381. AND ue.timestart < :now1 AND (ue.timeend = 0 OR ue.timeend > :now2)";
  382. $params = array('enabled'=>ENROL_INSTANCE_ENABLED, 'active'=>ENROL_USER_ACTIVE,
  383. 'userid'=>$USER->id, 'courseid'=>$coursecontext->instanceid, 'now1'=>$now, 'now2'=>$now);
  384. $until = $DB->get_field_sql($sql, $params);
  385. if (!$until or $until > time() + ENROL_REQUIRE_LOGIN_CACHE_PERIOD) {
  386. $until = time() + ENROL_REQUIRE_LOGIN_CACHE_PERIOD;
  387. }
  388. $USER->enrol['enrolled'][$course->id] = $until;
  389. $access = true;
  390. // remove traces of previous temp guest access
  391. if ($coursecontext->instanceid !== SITEID) {
  392. remove_temp_course_roles($coursecontext);
  393. }
  394. } else {
  395. $instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'status'=>ENROL_INSTANCE_ENABLED), 'sortorder, id ASC');
  396. $enrols = enrol_get_plugins(true);
  397. // first ask all enabled enrol instances in course if they want to auto enrol user
  398. foreach($instances as $instance) {
  399. if (!isset($enrols[$instance->enrol])) {
  400. continue;
  401. }
  402. // Get a duration for the guestaccess, a timestamp in the future or false.
  403. $until = $enrols[$instance->enrol]->try_autoenrol($instance);
  404. if ($until !== false) {
  405. $USER->enrol['enrolled'][$course->id] = $until;
  406. $USER->access = remove_temp_roles($coursecontext, $USER->access);
  407. $access = true;
  408. break;
  409. }
  410. }
  411. // if not enrolled yet try to gain temporary guest access
  412. if (!$access) {
  413. foreach($instances as $instance) {
  414. if (!isset($enrols[$instance->enrol])) {
  415. continue;
  416. }
  417. // Get a duration for the guestaccess, a timestamp in the future or false.
  418. $until = $enrols[$instance->enrol]->try_guestaccess($instance);
  419. if ($until !== false) {
  420. $USER->enrol['tempguest'][$course->id] = $until;
  421. $access = true;
  422. break;
  423. }
  424. }
  425. }
  426. }
  427. }
  428. if (!$access) {
  429. if ($preventredirect) {
  430. throw new require_login_exception('Not enrolled');
  431. }
  432. $SESSION->wantsurl = $FULLME;
  433. redirect($CFG->wwwroot .'/enrol/index.php?id='. $course->id);
  434. }
  435. }
  436. // Check visibility of activity to current user; includes visible flag, groupmembersonly,
  437. // conditional availability, etc
  438. if ($cm && !$cm->uservisible) {
  439. if ($preventredirect) {
  440. throw new require_login_exception('Activity is hidden');
  441. }
  442. redirect($CFG->wwwroot, get_string('activityiscurrentlyhidden'));
  443. }
  444. }
  445. class krystle_expand_navigation extends global_navigation {
  446. /** @var array */
  447. protected $expandable = array();
  448. private $expandtocourses = true;
  449. private $expandedcourses = array();
  450. // Added in 2.6, so we need to specify these here so that earlier versions don't complain.
  451. /** @var int site admin branch node type, used only within settings nav 71 */
  452. const TYPE_SITE_ADMIN = 71;
  453. /** var int Category displayed in MyHome navigation node */
  454. const TYPE_MY_CATEGORY = 11;
  455. /**
  456. * Constructs the navigation for use in AJAX request
  457. */
  458. public function __construct($page, $branchtype, $id) {
  459. $this->page = $page;
  460. $this->cache = new navigation_cache(NAVIGATION_CACHE_NAME);
  461. $this->children = new navigation_node_collection();
  462. $this->branchtype = $branchtype;
  463. $this->instanceid = $id;
  464. $this->initialise();
  465. }
  466. /**
  467. * Initialise the navigation given the type and id for the branch to expand.
  468. *
  469. * @param int $branchtype One of navigation_node::TYPE_*
  470. * @param int $id
  471. * @return array The expandable nodes
  472. */
  473. public function initialise() {
  474. global $CFG, $DB, $SITE, $PAGE;
  475. if ($this->initialised || during_initial_install()) {
  476. return $this->expandable;
  477. }
  478. $this->initialised = true;
  479. $this->rootnodes = array();
  480. $this->rootnodes['site'] = $this->add_course($SITE);
  481. $this->rootnodes['currentcourse'] = $this->add(get_string('currentcourse'), null, self::TYPE_ROOTNODE, null, 'currentcourse');
  482. $this->rootnodes['mycourses'] = $this->add(get_string('mycourses'), new moodle_url('/my'), self::TYPE_ROOTNODE, null, 'mycourses');
  483. $this->rootnodes['courses'] = $this->add(get_string('courses'), null, self::TYPE_ROOTNODE, null, 'courses');
  484. if (!empty($PAGE->theme->settings->coursesleafonly) || (!empty($PAGE->theme->settings->coursesloggedinonly) && !isloggedin())) {
  485. $this->expandtocourses = false;
  486. }
  487. if(function_exists('enrol_user_sees_own_courses')) {
  488. // Determine if the user is enrolled in any course.
  489. $enrolledinanycourse = enrol_user_sees_own_courses();
  490. if ($enrolledinanycourse) {
  491. $this->rootnodes['mycourses']->isexpandable = true;
  492. if ($CFG->navshowallcourses) {
  493. // When we show all courses we need to show both the my courses and the regular courses branch.
  494. $this->rootnodes['courses']->isexpandable = true;
  495. }
  496. } else {
  497. $this->rootnodes['courses']->isexpandable = true;
  498. }
  499. }
  500. $PAGE->requires->data_for_js('siteadminexpansion', false);
  501. $this->expand($this->branchtype, $this->instanceid);
  502. }
  503. public function expand($branchtype, $id) {
  504. global $CFG, $DB, $PAGE;
  505. static $krystle_course_activities = array();
  506. // Branchtype will be one of navigation_node::TYPE_*
  507. switch ($branchtype) {
  508. case self::TYPE_ROOTNODE :
  509. case self::TYPE_SITE_ADMIN :
  510. if ($id === 'mycourses') {
  511. $this->rootnodes['mycourses']->isexpandable = true;
  512. $this->load_courses_enrolled();
  513. } else if ($id === 'courses') {
  514. if ($this->expandtocourses) {
  515. $this->rootnodes['courses']->isexpandable = true;
  516. $this->load_courses_other();
  517. } else {
  518. // Don't load courses - theme settings say we shouldn't.
  519. $this->rootnodes['courses']->isexpandable = false;
  520. $this->rootnodes['courses']->nodetype = self::NODETYPE_LEAF;
  521. }
  522. }
  523. break;
  524. case self::TYPE_CATEGORY :
  525. case self::TYPE_MY_CATEGORY :
  526. if (!empty($PAGE->theme->settings->coursesleafonly)) {
  527. return false;
  528. }
  529. $this->load_all_categories($id);
  530. $limit = 20;
  531. if (!empty($CFG->navcourselimit)) {
  532. $limit = (int)$CFG->navcourselimit;
  533. }
  534. $courses = $DB->get_records('course', array('category' => $id), 'sortorder','*', 0, $limit);
  535. foreach ($courses as $course) {
  536. $this->add_course($course);
  537. }
  538. break;
  539. case self::TYPE_COURSE :
  540. $course = $DB->get_record('course', array('id' => $id), '*', MUST_EXIST);
  541. try {
  542. if(!array_key_exists($course->id, $this->expandedcourses)) {
  543. $coursenode = $this->add_course($course);
  544. if (!$coursenode) {
  545. break;
  546. }
  547. if ($PAGE->course->id !== $course->id) {
  548. $coursenode->nodetype = navigation_node::NODETYPE_LEAF;
  549. $coursenode->isexpandable = false;
  550. break;
  551. }
  552. $this->page->set_context(context_course::instance($course->id));
  553. $this->add_course_essentials($coursenode, $course);
  554. if ($PAGE->course->id == $course->id && (!method_exists($this, 'format_display_course_content') || $this->format_display_course_content($course->format))) {
  555. krystle_require_course_login($course);
  556. $this->expandedcourses[$course->id] = $this->expand_course($course, $coursenode);
  557. }
  558. }
  559. } catch(require_login_exception $rle) {
  560. $coursenode = $this->add_course($course);
  561. }
  562. break;
  563. case self::TYPE_SECTION :
  564. $sql = 'SELECT c.*, cs.section AS sectionnumber
  565. FROM {course} c
  566. LEFT JOIN {course_sections} cs ON cs.course = c.id
  567. WHERE cs.id = ?';
  568. $course = $DB->get_record_sql($sql, array($id), MUST_EXIST);
  569. try {
  570. $this->page->set_context(context_course::instance($course->id));
  571. if(!array_key_exists($course->id, $this->expandedcourses)) {
  572. $coursenode = $this->add_course($course);
  573. if (!$coursenode) {
  574. break;
  575. }
  576. $this->add_course_essentials($coursenode, $course);
  577. $this->expandedcourses[$course->id] = $this->expand_course($course, $coursenode);
  578. }
  579. if (property_exists($PAGE->theme->settings, 'expandtoactivities') && $PAGE->theme->settings->expandtoactivities) {
  580. if(!array_key_exists($course->id, $krystle_course_activities)) {
  581. list($sectionarray, $activities) = $this->generate_sections_and_activities($course);
  582. $krystle_course_activities[$course->id] = $activities;
  583. }
  584. $sections = $this->expandedcourses[$course->id];
  585. $activities = $krystle_course_activities[$course->id];
  586. if (!array_key_exists($course->sectionnumber, $sections)) break;
  587. $section = $sections[$course->sectionnumber];
  588. if (is_null($section) || is_null($section->sectionnode)) break;
  589. $activitynodes = $this->load_section_activities($section->sectionnode, $course->sectionnumber, $activities);
  590. foreach ($activitynodes as $id=>$node) {
  591. // load all section activities now
  592. $cm_stub = new stdClass();
  593. $cm_stub->id = $id;
  594. $this->load_activity($cm_stub, $course, $node);
  595. }
  596. }
  597. } catch(require_login_exception $rle) {
  598. $coursenode = $this->add_course($course);
  599. }
  600. break;
  601. case self::TYPE_ACTIVITY :
  602. // Now expanded above, as part of the section expansion
  603. break;
  604. default:
  605. throw new Exception('Unknown type');
  606. return $this->expandable;
  607. }
  608. return $this->expandable;
  609. }
  610. private function expand_course(stdClass $course, navigation_node $coursenode) {
  611. $sectionnodes = $this->load_course_sections($course, $coursenode);
  612. if (empty($sectionnodes)) {
  613. // 2.4 compat - load_course_sections no longer returns the list of sections
  614. $sectionnodes = array();
  615. foreach ($coursenode->children as $node) {
  616. if($node->type != self::TYPE_SECTION) continue;
  617. $section = new stdClass();
  618. $section->sectionnode = $node;
  619. // Get section number out of action URL (it doesn't seem to be available elsewhere!)
  620. $hasparams = false;
  621. if (!empty($node->action) && $params = $node->action->params()){
  622. $hasparams = true;
  623. }
  624. if ($hasparams && array_key_exists('section', $params)) {
  625. $sectionnodes[$params['section']] = $section;
  626. } else {
  627. $sectionnodes[] = $section;
  628. }
  629. }
  630. $expanded = $sectionnodes;
  631. }
  632. return $sectionnodes;
  633. }
  634. /**
  635. * Loads all of the activities for a section into the navigation structure.
  636. *
  637. * @param navigation_node $sectionnode
  638. * @param int $sectionnumber
  639. * @param array $activities An array of activites as returned by {@link global_navigation::generate_sections_and_activities()}
  640. * @param stdClass $course The course object the section and activities relate to.
  641. * @return array Array of activity nodes
  642. */
  643. protected function load_section_activities(navigation_node $sectionnode, $sectionnumber, array $activities, $course = null) {
  644. global $CFG, $SITE;
  645. // A static counter for JS function naming
  646. static $legacyonclickcounter = 0;
  647. $activitynodes = array();
  648. if (empty($activities)) {
  649. return $activitynodes;
  650. }
  651. if (!is_object($course)) {
  652. $activity = reset($activities);
  653. $courseid = $activity->course;
  654. } else {
  655. $courseid = $course->id;
  656. }
  657. $showactivities = ($courseid != $SITE->id || !empty($CFG->navshowfrontpagemods));
  658. foreach ($activities as $activity) {
  659. if ($activity->section != $sectionnumber) {
  660. continue;
  661. }
  662. if ($activity->icon) {
  663. $icon = new pix_icon($activity->icon, get_string('modulename', $activity->modname), $activity->iconcomponent);
  664. } else {
  665. $icon = new pix_icon('icon', get_string('modulename', $activity->modname), $activity->modname);
  666. }
  667. // Prepare the default name and url for the node
  668. $activityname = format_string($activity->name, true, array('context' => context_module::instance($activity->id)));
  669. $action = new moodle_url($activity->url);
  670. // Legacy onclick removed from krystle - clicking in Awesomebar should go to the page, not trigger popups etc.
  671. $activitynode = $sectionnode->add($activityname, $action, navigation_node::TYPE_ACTIVITY, null, $activity->id, $icon);
  672. $activitynode->title(get_string('modulename', $activity->modname));
  673. $activitynode->hidden = $activity->hidden;
  674. $activitynode->display = $showactivities && $activity->display;
  675. $activitynode->nodetype = $activity->nodetype;
  676. $activitynodes[$activity->id] = $activitynode;
  677. }
  678. return $activitynodes;
  679. }
  680. /**
  681. * They've expanded the 'my courses' branch.
  682. */
  683. protected function load_courses_enrolled() {
  684. global $DB;
  685. $courses = enrol_get_my_courses();
  686. if ($this->show_my_categories(true)) {
  687. // OK Actually we are loading categories. We only want to load categories that have a parent of 0.
  688. // In order to make sure we load everything required we must first find the categories that are not
  689. // base categories and work out the bottom category in thier path.
  690. $categoryids = array();
  691. $toplevelcats = array();
  692. foreach ($courses as $course) {
  693. $categoryids[] = $course->category;
  694. $toplevelcats[$course->category] = $course->category;
  695. }
  696. $categoryids = array_unique($categoryids);
  697. list($sql, $params) = $DB->get_in_or_equal($categoryids);
  698. $categories = $DB->get_recordset_select('course_categories', 'id '.$sql.' AND parent <> 0', $params, 'sortorder, id', 'id, path');
  699. foreach ($categories as $category) {
  700. $bits = explode('/', trim($category->path,'/'));
  701. $toplevelcats[$category->id] = $bits[0];
  702. $categoryids[] = array_shift($bits);
  703. }
  704. $categoryids = array_unique($categoryids);
  705. $categories->close();
  706. // Now we load the base categories.
  707. list($sql, $params) = $DB->get_in_or_equal($categoryids);
  708. $categories = $DB->get_recordset_select('course_categories', 'id '.$sql.' AND parent = 0', $params, 'sortorder, id');
  709. foreach ($categories as $category) {
  710. $this->add_category($category, $this->rootnodes['mycourses']);
  711. }
  712. $categories->close();
  713. foreach ($courses as $course) {
  714. $cat = $this->rootnodes['mycourses']->find($toplevelcats[$course->category], self::TYPE_CATEGORY);
  715. $node = $this->add_course_to($course, false, self::COURSE_MY, $cat);
  716. }
  717. } else {
  718. foreach ($courses as $course) {
  719. $node = $this->add_course($course, false, self::COURSE_MY);
  720. if (!$this->rootnodes['mycourses']->find($node->key, self::TYPE_COURSE)) {
  721. // Hasn't been added to this node
  722. $this->rootnodes['mycourses']->add_node($node);
  723. }
  724. }
  725. }
  726. }
  727. /**
  728. * Adds a structured category to the navigation in the correct order/place
  729. *
  730. * @param stdClass $category
  731. * @param navigation_node $parent
  732. */
  733. protected function add_category(stdClass $category, navigation_node $parent, $nodetype = self::TYPE_CATEGORY) {
  734. if ((!$this->expandtocourses && $parent->key=='courses') || $parent->find($category->id, self::TYPE_CATEGORY)) {
  735. return;
  736. }
  737. $url = new moodle_url('/course/category.php', array('id' => $category->id));
  738. $context = context_coursecat::instance($category->id);
  739. $categoryname = format_string($category->name, true, array('context' => $context));
  740. $categorynode = $parent->add($categoryname, $url, $nodetype, $categoryname, $category->id);
  741. if (empty($category->visible)) {
  742. if (has_capability('moodle/category:viewhiddencategories', context_system::instance())) {
  743. $categorynode->hidden = true;
  744. } else {
  745. $categorynode->display = false;
  746. }
  747. }
  748. $this->addedcategories[$category->id] = $categorynode;
  749. }
  750. /**
  751. * Adds the given course to the navigation structure, under a specified parent.
  752. *
  753. * @param stdClass $course
  754. * @param bool $forcegeneric
  755. * @param bool $ismycourse
  756. * @return navigation_node
  757. */
  758. public function add_course_to(stdClass $course, $forcegeneric = false, $coursetype = self::COURSE_OTHER, navigation_node $parent) {
  759. global $CFG, $SITE;
  760. $coursecontext = context_course::instance($course->id);
  761. if ($course->id != $SITE->id && !$course->visible) {
  762. if (is_role_switched($course->id)) {
  763. // user has to be able to access course in order to switch, let's skip the visibility test here
  764. } else if (!has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
  765. return false;
  766. }
  767. }
  768. $shortname = format_string($course->shortname, true, array('context' => $coursecontext));
  769. $url = new moodle_url('/course/view.php', array('id'=>$course->id));
  770. $coursenode = $parent->add($shortname, $url, self::TYPE_COURSE, $shortname, $course->id);
  771. $coursenode->nodetype = self::NODETYPE_BRANCH;
  772. $coursenode->hidden = (!$course->visible);
  773. $coursenode->title(format_string($course->fullname, true, array('context' => $coursecontext)));
  774. return $coursenode;
  775. }
  776. public function add_course(stdClass $course, $forcegeneric = false, $coursetype = self::COURSE_OTHER) {
  777. global $PAGE;
  778. if (!$forcegeneric && array_key_exists($course->id, $this->addedcourses)) {
  779. return $this->addedcourses[$course->id];
  780. }
  781. if ($coursetype == self::COURSE_OTHER && $PAGE->course->id == $course->id) {
  782. $coursetype = self::COURSE_CURRENT;
  783. }
  784. if ($this->expandtocourses || $coursetype == self::COURSE_MY || $coursetype == self::COURSE_CURRENT) {
  785. return parent::add_course($course, $forcegeneric, $coursetype);
  786. }
  787. return false;
  788. }
  789. /**
  790. * They've expanded the general 'courses' branch.
  791. */
  792. protected function load_courses_other() {
  793. if (!$this->expandtocourses) {
  794. return;
  795. }
  796. $this->load_all_courses();
  797. }
  798. protected function load_all_courses($categoryids = null) {
  799. if (!$this->expandtocourses) {
  800. return array();
  801. }
  802. return parent::load_all_courses($categoryids);
  803. }
  804. public function get_expandable() {
  805. return $this->expandable;
  806. }
  807. }
  808. class krystle_dummy_page extends moodle_page {
  809. /**
  810. * REALLY Set the main context to which this page belongs.
  811. * @param object $context a context object, normally obtained with context_XXX::instance.
  812. */
  813. public function set_context($context) {
  814. if ($context === null) {
  815. // extremely ugly hack which sets context to some value in order to prevent warnings,
  816. // use only for core error handling!!!!
  817. if (!$this->_context) {
  818. $this->_context = context_system::instance();
  819. }
  820. return;
  821. }
  822. $this->_context = $context;
  823. }
  824. }