129
132
/** @var bool Set to true if we KNOW that this node can be expanded. */
130
133
public $isexpandable = false;
131
134
/** @var array */
132
protected $namedtypes = array(0=>'system',10=>'category',20=>'course',30=>'structure',40=>'activity',50=>'resource',60=>'custom',70=>'setting', 80=>'user');
135
protected $namedtypes = array(0=>'system',10=>'category',20=>'course',30=>'structure',40=>'activity',50=>'resource',60=>'custom',70=>'setting',71=>'siteadmin', 80=>'user');
133
136
/** @var moodle_url */
134
137
protected static $fullmeurl = null;
135
138
/** @var bool toogles auto matching of active node */
136
139
public static $autofindactive = true;
140
/** @var bool should we load full admin tree or rely on AJAX for performance reasons */
141
protected static $loadadmintree = false;
137
142
/** @var mixed If set to an int, that section will be included even if it has no activities */
138
143
public $includesectionnum = false;
237
242
* is either $PAGE->url or if that hasn't been set $FULLME.
239
244
* @param moodle_url $url The url to use for the fullmeurl.
245
* @param bool $loadadmintree use true if the URL point to administration tree
241
public static function override_active_url(moodle_url $url) {
247
public static function override_active_url(moodle_url $url, $loadadmintree = false) {
242
248
// Clone the URL, in case the calling script changes their URL later.
243
249
self::$fullmeurl = new moodle_url($url);
250
// True means we do not want AJAX loaded admin tree, required for all admin pages.
251
if ($loadadmintree) {
252
// Do not change back to false if already set.
253
self::$loadadmintree = true;
258
* Use when page is linked from the admin tree,
259
* if not used navigation could not find the page using current URL
260
* because the tree is not fully loaded.
262
public static function require_admin_tree() {
263
self::$loadadmintree = true;
324
344
// If added node is a category node or the user is logged in and it's a course
325
345
// then mark added node as a branch (makes it expandable by AJAX)
326
346
$type = $childnode->type;
327
if (($type == self::TYPE_CATEGORY) || (isloggedin() && ($type == self::TYPE_COURSE)) || ($type == self::TYPE_MY_CATEGORY)) {
347
if (($type == self::TYPE_CATEGORY) || (isloggedin() && ($type == self::TYPE_COURSE)) || ($type == self::TYPE_MY_CATEGORY) ||
348
($type === self::TYPE_SITE_ADMIN)) {
328
349
$node->nodetype = self::NODETYPE_BRANCH;
330
351
// If this node is hidden mark it's children as hidden also
1254
1275
// Give the local plugins a chance to include some navigation if they want.
1255
foreach (get_plugin_list_with_file('local', 'lib.php', true) as $plugin => $file) {
1276
foreach (core_component::get_plugin_list_with_file('local', 'lib.php', true) as $plugin => $file) {
1256
1277
$function = "local_{$plugin}_extends_navigation";
1257
1278
$oldfunction = "{$plugin}_extends_navigation";
1258
1279
if (function_exists($function)) {
1426
1447
if (count($fullfetch)) {
1427
1448
// First up fetch all of the courses in categories where we know that we are going to
1428
1449
// need the majority of courses.
1429
list($ccselect, $ccjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
1430
1450
list($categoryids, $categoryparams) = $DB->get_in_or_equal($fullfetch, SQL_PARAMS_NAMED, 'lcategory');
1451
$ccselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
1452
$ccjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel)";
1453
$categoryparams['contextlevel'] = CONTEXT_COURSE;
1431
1454
$sql = "SELECT c.id, c.sortorder, c.visible, c.fullname, c.shortname, c.category $ccselect
1432
1455
FROM {course} c
1461
1484
// Next we will work our way through the categories where we will likely only need a small
1462
1485
// proportion of the courses.
1463
1486
foreach ($partfetch as $categoryid) {
1464
list($ccselect, $ccjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
1487
$ccselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
1488
$ccjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel)";
1465
1489
$sql = "SELECT c.id, c.sortorder, c.visible, c.fullname, c.shortname, c.category $ccselect
1466
1490
FROM {course} c
1468
1492
WHERE c.category = :categoryid
1469
1493
ORDER BY c.sortorder ASC";
1470
$courseparams = array('categoryid' => $categoryid);
1494
$courseparams = array('categoryid' => $categoryid, 'contextlevel' => CONTEXT_COURSE);
1471
1495
$coursesrs = $DB->get_recordset_sql($sql, $courseparams, 0, $limit * 5);
1472
1496
foreach ($coursesrs as $course) {
1473
1497
if ($course->id == $SITE->id) {
1497
1521
// Prepare the SQL to load the courses and their contexts
1498
list($ccselect, $ccjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
1499
1522
list($courseids, $courseparams) = $DB->get_in_or_equal(array_keys($this->addedcourses), SQL_PARAMS_NAMED, 'lc', false);
1523
$ccselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
1524
$ccjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel)";
1525
$courseparams['contextlevel'] = CONTEXT_COURSE;
1500
1526
$sql = "SELECT c.id, c.sortorder, c.visible, c.fullname, c.shortname, c.category $ccselect
1501
1527
FROM {course} c
1728
1754
$categoryname = format_string($category->name, true, array('context' => $context));
1729
1755
$categorynode = $parent->add($categoryname, $url, $nodetype, $categoryname, $category->id);
1730
1756
if (empty($category->visible)) {
1731
if (has_capability('moodle/category:viewhiddencategories', get_system_context())) {
1757
if (has_capability('moodle/category:viewhiddencategories', context_system::instance())) {
1732
1758
$categorynode->hidden = true;
1734
1760
$categorynode->display = false;
2179
2205
if (!empty($CFG->messaging)) {
2180
$messageargs = null;
2206
$messageargs = array('user1' => $USER->id);
2181
2207
if ($USER->id != $user->id) {
2182
$messageargs = array('user1' => $user->id);
2208
$messageargs['user2'] = $user->id;
2210
if ($course->id != $SITE->id) {
2211
$messageargs['viewing'] = MESSAGE_VIEW_COURSE. $course->id;
2184
2213
$url = new moodle_url('/message/index.php',$messageargs);
2185
2214
$usernode->add(get_string('messages', 'message'), $url, self::TYPE_SETTING, null, 'messages');
2223
2252
} else if ($course->id == $SITE->id && has_capability('moodle/user:viewdetails', $usercontext) && (!in_array('mycourses', $hiddenfields) || has_capability('moodle/user:viewhiddendetails', $coursecontext))) {
2225
2254
// Add view grade report is permitted
2226
$reports = get_plugin_list('gradereport');
2255
$reports = core_component::get_plugin_list('gradereport');
2227
2256
arsort($reports); // user is last, we want to test it first
2229
2258
$userscourses = enrol_get_users_courses($user->id);
2482
2511
$coursenode->add('frontpageloaded', null, self::TYPE_CUSTOM, null, 'frontpageloaded')->display = false;
2485
if (has_capability('moodle/course:viewparticipants', get_system_context())) {
2514
if (has_capability('moodle/course:viewparticipants', context_system::instance())) {
2486
2515
$coursenode->add(get_string('participants'), new moodle_url('/user/index.php?id='.$course->id), self::TYPE_CUSTOM, get_string('participants'), 'participants');
2719
2748
$this->rootnodes['site'] = $this->add_course($SITE);
2720
2749
$this->rootnodes['mycourses'] = $this->add(get_string('mycourses'), new moodle_url('/my'), self::TYPE_ROOTNODE, null, 'mycourses');
2721
2750
$this->rootnodes['courses'] = $this->add(get_string('courses'), null, self::TYPE_ROOTNODE, null, 'courses');
2751
// The courses branch is always displayed, and is always expandable (although may be empty).
2752
// This mimicks what is done during {@link global_navigation::initialise()}.
2753
$this->rootnodes['courses']->isexpandable = true;
2723
2755
// Branchtype will be one of navigation_node::TYPE_*
2724
2756
switch ($this->branchtype) {
3178
3222
$this->children[] = new navigation_node($itemarray);
3227
* Prepends a new navigation_node to the start of the navbar
3229
* @param string $text
3230
* @param string|moodle_url|action_link $action An action to associate with this node.
3231
* @param int $type One of navigation_node::TYPE_*
3232
* @param string $shorttext
3233
* @param string|int $key A key to identify this node with. Key + type is unique to a parent.
3234
* @param pix_icon $icon An optional icon to use for this node.
3235
* @return navigation_node
3237
public function prepend($text, $action=null, $type=self::TYPE_CUSTOM, $shorttext=null, $key=null, pix_icon $icon=null) {
3238
if ($this->content !== null) {
3239
debugging('Nav bar items must be printed before $OUTPUT->header() has been called', DEBUG_DEVELOPER);
3241
// Properties array used when creating the new navigation node.
3246
// Set the action if one was provided.
3247
if ($action!==null) {
3248
$itemarray['action'] = $action;
3250
// Set the shorttext if one was provided.
3251
if ($shorttext!==null) {
3252
$itemarray['shorttext'] = $shorttext;
3254
// Set the icon if one was provided.
3256
$itemarray['icon'] = $icon;
3258
// Default the key to the number of children if not provided.
3259
if ($key === null) {
3260
$key = count($this->children);
3263
$itemarray['key'] = $key;
3264
// Set the parent to this node.
3265
$itemarray['parent'] = $this;
3266
// Add the child node to the prepend list.
3267
$this->prependchildren[] = new navigation_node($itemarray);
3276
$settings = $this->load_user_settings($this->page->course->id);
3278
if (isloggedin() && !isguestuser() && (!property_exists($SESSION, 'load_navigation_admin') || $SESSION->load_navigation_admin)) {
3279
$admin = $this->load_administration_settings();
3280
$SESSION->load_navigation_admin = ($admin->has_children());
3364
$usersettings = $this->load_user_settings($this->page->course->id);
3366
$adminsettings = false;
3367
if (isloggedin() && !isguestuser() && (!isset($SESSION->load_navigation_admin) || $SESSION->load_navigation_admin)) {
3368
$isadminpage = $this->is_admin_tree_needed();
3370
if (has_capability('moodle/site:config', context_system::instance())) {
3371
// Make sure this works even if config capability changes on the fly
3372
// and also make it fast for admin right after login.
3373
$SESSION->load_navigation_admin = 1;
3375
$adminsettings = $this->load_administration_settings();
3378
} else if (!isset($SESSION->load_navigation_admin)) {
3379
$adminsettings = $this->load_administration_settings();
3380
$SESSION->load_navigation_admin = (int)($adminsettings->children->count() > 0);
3382
} else if ($SESSION->load_navigation_admin) {
3384
$adminsettings = $this->load_administration_settings();
3388
// Print empty navigation node, if needed.
3389
if ($SESSION->load_navigation_admin && !$isadminpage) {
3390
if ($adminsettings) {
3391
// Do not print settings tree on pages that do not need it, this helps with performance.
3392
$adminsettings->remove();
3393
$adminsettings = false;
3395
$siteadminnode = $this->add(get_string('administrationsite'), new moodle_url('/admin'), self::TYPE_SITE_ADMIN, null, 'siteadministration');
3396
$siteadminnode->id = 'expandable_branch_'.$siteadminnode->type.'_'.clean_param($siteadminnode->key, PARAM_ALPHANUMEXT);
3397
$this->page->requires->data_for_js('siteadminexpansion', $siteadminnode);
3285
if ($context->contextlevel == CONTEXT_SYSTEM && $admin) {
3286
$admin->force_open();
3287
} else if ($context->contextlevel == CONTEXT_USER && $settings) {
3288
$settings->force_open();
3401
if ($context->contextlevel == CONTEXT_SYSTEM && $adminsettings) {
3402
$adminsettings->force_open();
3403
} else if ($context->contextlevel == CONTEXT_USER && $usersettings) {
3404
$usersettings->force_open();
3291
3407
// Check if the user is currently logged in as another user
3292
if (session_is_loggedinas()) {
3408
if (\core\session\manager::is_loggedinas()) {
3293
3409
// Get the actual user, we need this so we can display an informative return link
3294
$realuser = session_get_realuser();
3410
$realuser = \core\session\manager::get_realuser();
3295
3411
// Add the informative return to original user link
3296
3412
$url = new moodle_url('/course/loginas.php',array('id'=>$this->page->course->id, 'return'=>1,'sesskey'=>sesskey()));
3297
3413
$this->add(get_string('returntooriginaluser', 'moodle', fullname($realuser, true)), $url, self::TYPE_SETTING, null, null, new pix_icon('t/left', ''));
3475
* Does this page require loading of full admin tree or is
3476
* it enough rely on AJAX?
3480
protected function is_admin_tree_needed() {
3481
if (self::$loadadmintree) {
3482
// Usually external admin page or settings page.
3486
if ($this->page->pagelayout === 'admin' or strpos($this->page->pagetype, 'admin-') === 0) {
3487
// Admin settings tree is intended for system level settings and management only, use navigation for the rest!
3488
if ($this->page->context->contextlevel != CONTEXT_SYSTEM) {
3354
3498
* Load the site administration tree
3378
3522
// Disable the navigation from automatically finding the active node
3379
3523
navigation_node::$autofindactive = false;
3380
$referencebranch = $this->add(get_string('administrationsite'), null, self::TYPE_SETTING, null, 'root');
3524
$referencebranch = $this->add(get_string('administrationsite'), null, self::TYPE_SITE_ADMIN, null, 'root');
3381
3525
foreach ($adminroot->children as $adminbranch) {
3382
3526
$this->load_administration_settings($referencebranch, $adminbranch);
3468
* Generate the list of modules for the given course.
3470
* @param stdClass $course The course to get modules for
3472
protected function get_course_modules($course) {
3474
// This function is included when we include course/lib.php at the top
3476
$modnames = get_module_types_names();
3477
$resources = array();
3478
$activities = array();
3479
foreach($modnames as $modname=>$modnamestr) {
3480
if (!course_allowed_module($course, $modname)) {
3484
$libfile = "$CFG->dirroot/mod/$modname/lib.php";
3485
if (!file_exists($libfile)) {
3488
include_once($libfile);
3489
$gettypesfunc = $modname.'_get_types';
3490
if (function_exists($gettypesfunc)) {
3491
$types = $gettypesfunc();
3492
foreach($types as $type) {
3493
if (!isset($type->modclass) || !isset($type->typestr)) {
3494
debugging('Incorrect activity type in '.$modname);
3497
if ($type->modclass == MOD_CLASS_RESOURCE) {
3498
$resources[html_entity_decode($type->type, ENT_QUOTES, 'UTF-8')] = $type->typestr;
3500
$activities[html_entity_decode($type->type, ENT_QUOTES, 'UTF-8')] = $type->typestr;
3504
$archetype = plugin_supports('mod', $modname, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER);
3505
if ($archetype == MOD_ARCHETYPE_RESOURCE) {
3506
$resources[$modname] = $modnamestr;
3508
// all other archetypes are considered activity
3509
$activities[$modname] = $modnamestr;
3513
return array($resources, $activities);
3517
3612
* This function loads the course settings that are available for the user
3519
3614
* @param bool $forceopen If set to true the course node will be forced open
3580
3675
if (has_capability('moodle/site:viewreports', $coursecontext)) { // Basic capability for listing of reports.
3581
3676
$reportnav = $coursenode->add(get_string('reports'), null, self::TYPE_CONTAINER, null, null,
3582
3677
new pix_icon('i/stats', ''));
3583
$coursereports = get_plugin_list('coursereport');
3678
$coursereports = core_component::get_plugin_list('coursereport');
3584
3679
foreach ($coursereports as $report => $dir) {
3585
3680
$libfile = $CFG->dirroot.'/course/report/'.$report.'/lib.php';
3586
3681
if (file_exists($libfile)) {
3603
3698
if (has_capability('moodle/grade:viewall', $coursecontext)) {
3604
3699
$reportavailable = true;
3605
3700
} else if (!empty($course->showgrades)) {
3606
$reports = get_plugin_list('gradereport');
3701
$reports = core_component::get_plugin_list('gradereport');
3607
3702
if (is_array($reports) && count($reports)>0) { // Get all installed reports
3608
3703
arsort($reports); // user is last, we want to test it first
3609
3704
foreach ($reports as $plugin => $plugindir) {
3750
3845
// Settings for the module
3751
3846
if (has_capability('moodle/course:manageactivities', $this->page->cm->context)) {
3752
$url = new moodle_url('/course/modedit.php', array('update' => $this->page->cm->id, 'return' => true, 'sesskey' => sesskey()));
3847
$url = new moodle_url('/course/modedit.php', array('update' => $this->page->cm->id, 'return' => 1));
3753
3848
$modulenode->add(get_string('editsettings'), $url, navigation_node::TYPE_SETTING, null, 'modedit');
3755
3850
// Assign local roles
4019
4114
// Change password link
4020
if ($userauthplugin && $currentuser && !session_is_loggedinas() && !isguestuser() && has_capability('moodle/user:changeownpassword', $systemcontext) && $userauthplugin->can_change_password()) {
4115
if ($userauthplugin && $currentuser && !\core\session\manager::is_loggedinas() && !isguestuser() && has_capability('moodle/user:changeownpassword', $systemcontext) && $userauthplugin->can_change_password()) {
4021
4116
$passwordchangeurl = $userauthplugin->change_password_url();
4022
4117
if (empty($passwordchangeurl)) {
4023
4118
$passwordchangeurl = new moodle_url('/login/change_password.php', array('id'=>$course->id));
4052
4147
if ($currentuser && !empty($CFG->enableportfolios) && has_capability('moodle/portfolio:export', $systemcontext)) {
4053
4148
require_once($CFG->libdir . '/portfoliolib.php');
4054
if (portfolio_instances(true, false)) {
4149
if (portfolio_has_visible_instances()) {
4055
4150
$portfolio = $usersetting->add(get_string('portfolios', 'portfolio'), null, self::TYPE_SETTING);
4057
4152
$url = new moodle_url('/user/portfolio.php', array('courseid'=>$course->id));
4135
4230
$reporttab->trim_if_empty();
4137
4232
// Login as ...
4138
if (!$user->deleted and !$currentuser && !session_is_loggedinas() && has_capability('moodle/user:loginas', $coursecontext) && !is_siteadmin($user->id)) {
4233
if (!$user->deleted and !$currentuser && !\core\session\manager::is_loggedinas() && has_capability('moodle/user:loginas', $coursecontext) && !is_siteadmin($user->id)) {
4139
4234
$url = new moodle_url('/course/loginas.php', array('id'=>$course->id, 'user'=>$user->id, 'sesskey'=>sesskey()));
4140
4235
$usersetting->add(get_string('loginas'), $url, self::TYPE_SETTING);
4151
4246
protected function load_block_settings() {
4154
$blocknode = $this->add(print_context_name($this->context), null, self::TYPE_SETTING, null, 'blocksettings');
4249
$blocknode = $this->add($this->context->get_context_name(), null, self::TYPE_SETTING, null, 'blocksettings');
4155
4250
$blocknode->force_open();
4157
4252
// Assign local roles
4180
4275
protected function load_category_settings() {
4183
$categorynode = $this->add(print_context_name($this->context), null, null, null, 'categorysettings');
4278
$categorynode = $this->add($this->context->get_context_name(), null, null, null, 'categorysettings');
4184
4279
$categorynode->force_open();
4185
$onmanagepage = $this->page->url->compare(new moodle_url('/course/manage.php'), URL_MATCH_BASE);
4187
if (can_edit_in_category($this->context->instanceid) && !$onmanagepage) {
4188
$url = new moodle_url('/course/manage.php', array('categoryid' => $this->context->instanceid));
4281
if (can_edit_in_category($this->context->instanceid)) {
4282
$url = new moodle_url('/course/management.php', array('categoryid' => $this->context->instanceid));
4189
4283
$editstring = get_string('managecategorythis');
4190
4284
$categorynode->add($editstring, $url, self::TYPE_SETTING, null, null, new pix_icon('i/edit', ''));
4219
if (has_capability('moodle/cohort:manage', $this->context) or has_capability('moodle/cohort:view', $this->context)) {
4313
if (has_any_capability(array('moodle/cohort:view', 'moodle/cohort:manage'), $this->context)) {
4220
4314
$categorynode->add(get_string('cohorts', 'cohort'), new moodle_url('/cohort/index.php', array('contextid' => $this->context->id)), self::TYPE_SETTING, null, 'cohort', new pix_icon('i/cohort', ''));
4305
4399
if (has_capability('moodle/site:viewreports', $coursecontext)) { // Basic capability for listing of reports.
4306
4400
$frontpagenav = $frontpage->add(get_string('reports'), null, self::TYPE_CONTAINER, null, null,
4307
4401
new pix_icon('i/stats', ''));
4308
$coursereports = get_plugin_list('coursereport');
4402
$coursereports = core_component::get_plugin_list('coursereport');
4309
4403
foreach ($coursereports as $report=>$dir) {
4310
4404
$libfile = $CFG->dirroot.'/course/report/'.$report.'/lib.php';
4311
4405
if (file_exists($libfile)) {
4342
4436
// Manage files
4343
4437
if ($course->legacyfiles == 2 and has_capability('moodle/course:managefiles', $this->context)) {
4344
4438
//hiden in new installs
4345
$url = new moodle_url('/files/index.php', array('contextid'=>$coursecontext->id, 'itemid'=>0, 'component' => 'course', 'filearea'=>'legacy'));
4439
$url = new moodle_url('/files/index.php', array('contextid'=>$coursecontext->id));
4346
4440
$frontpage->add(get_string('sitelegacyfiles'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/folder', ''));
4348
4442
return $frontpage;
4465
* Class used to populate site admin navigation for ajax.
4468
* @category navigation
4469
* @copyright 2013 Rajesh Taneja <rajesh@moodle.com>
4470
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4472
class settings_navigation_ajax extends settings_navigation {
4474
* Constructs the navigation for use in an AJAX request
4476
* @param moodle_page $page
4478
public function __construct(moodle_page &$page) {
4479
$this->page = $page;
4480
$this->cache = new navigation_cache(NAVIGATION_CACHE_NAME);
4481
$this->children = new navigation_node_collection();
4482
$this->initialise();
4486
* Initialise the site admin navigation.
4488
* @return array An array of the expandable nodes
4490
public function initialise() {
4491
if ($this->initialised || during_initial_install()) {
4494
$this->load_administration_settings();
4496
// Check if local plugins is adding node to site admin.
4497
$this->load_local_plugin_settings();
4499
$this->initialised = true;
4371
4504
* Simple class used to output a navigation branch in XML
4373
4506
* @package core