navigation MDL-21604 Fixed AJAX security issue + the following changes

* AJAX expansion now uses JSON rather than XML
* Tidied up unused globals in navigaitonlib after recent refactoring
* Cleaned up Database calls that were no longer required after security changes
This commit is contained in:
Sam Hemelryk
2010-02-18 05:57:20 +00:00
parent 9ae71e9cc0
commit 507a7a9a2d
5 changed files with 254 additions and 346 deletions

View File

@@ -83,7 +83,7 @@ class block_global_navigation_tree extends block_tree {
global $CFG; global $CFG;
$this->_initialise_dock(); $this->_initialise_dock();
$this->page->requires->js_module(array('name'=>'core_dock', 'fullpath'=>'/blocks/dock.js', 'requires'=>array('base', 'cookie', 'dom', 'io', 'node', 'event-custom', 'event-mouseenter', 'yui2-container'))); $this->page->requires->js_module(array('name'=>'core_dock', 'fullpath'=>'/blocks/dock.js', 'requires'=>array('base', 'cookie', 'dom', 'io', 'node', 'event-custom', 'event-mouseenter', 'yui2-container')));
$this->page->requires->js_module(array('name'=>'block_navigation', 'fullpath'=>'/blocks/global_navigation_tree/navigation.js', 'requires'=>array('core_dock', 'io', 'node', 'dom', 'event-custom'))); $this->page->requires->js_module(array('name'=>'block_navigation', 'fullpath'=>'/blocks/global_navigation_tree/navigation.js', 'requires'=>array('core_dock', 'io', 'node', 'dom', 'event-custom', 'json-parse')));
user_preference_allow_ajax_update('docked_block_instance_'.$this->instance->id, PARAM_INT); user_preference_allow_ajax_update('docked_block_instance_'.$this->instance->id, PARAM_INT);
} }
@@ -155,7 +155,7 @@ class block_global_navigation_tree extends block_tree {
$this->page->navigation->find_expandable($expandable); $this->page->navigation->find_expandable($expandable);
// Initialise the JS tree object // Initialise the JS tree object
$module = array('name'=>'block_navigation', 'fullpath'=>'/blocks/global_navigation_tree/navigation.js', 'requires'=>array('core_dock', 'io', 'node', 'dom', 'event-custom')); $module = array('name'=>'block_navigation', 'fullpath'=>'/blocks/global_navigation_tree/navigation.js', 'requires'=>array('core_dock', 'io', 'node', 'dom', 'event-custom', 'json-parse'));
$arguments = array($this->instance->id, array('expansions'=>$expandable, 'instance'=>$this->instance->id, 'candock'=>$this->instance_can_be_docked())); $arguments = array($this->instance->id, array('expansions'=>$expandable, 'instance'=>$this->instance->id, 'candock'=>$this->instance_can_be_docked()));
$this->page->requires->js_init_call('M.block_navigation.init_add_tree', $arguments, false, $module); $this->page->requires->js_init_call('M.block_navigation.init_add_tree', $arguments, false, $module);

View File

@@ -155,16 +155,16 @@ M.block_navigation.classes.tree.prototype.init_load_ajax = function(e, branch) {
* @return bool * @return bool
*/ */
M.block_navigation.classes.tree.prototype.load_ajax = function(tid, outcome, args) { M.block_navigation.classes.tree.prototype.load_ajax = function(tid, outcome, args) {
// Check the status try {
if (outcome.status!=0 && outcome.responseXML!=null) { var object = this.Y.JSON.parse(outcome.responseText);
var branch = outcome.responseXML.documentElement; if (this.add_branch(object, args.target.ancestor('LI') ,1)) {
if (branch!=null && this.add_branch(branch, args.target.ancestor('LI') ,1)) {
// If we get here everything worked perfectly
if (this.candock) { if (this.candock) {
M.core_dock.resize(); M.core_dock.resize();
} }
return true; return true;
} }
} catch (e) {
// If we got here then there was an error parsing the result
} }
// The branch is empty so class it accordingly // The branch is empty so class it accordingly
args.target.replaceClass('branch', 'emptybranch'); args.target.replaceClass('branch', 'emptybranch');
@@ -178,14 +178,14 @@ M.block_navigation.classes.tree.prototype.load_ajax = function(tid, outcome, arg
* @param {int} depth * @param {int} depth
* @return bool * @return bool
*/ */
M.block_navigation.classes.tree.prototype.add_branch = function(branchxml, target, depth) { M.block_navigation.classes.tree.prototype.add_branch = function(branchobj, target, depth) {
// Make the new branch into an object // Make the new branch into an object
var branch = new M.block_navigation.classes.branch(this, branchxml); var branch = new M.block_navigation.classes.branch(this, branchobj);
var childrenul = false; var childrenul = false;
if (depth === 1) { if (depth === 1) {
if (!branch.haschildren) { if (!branch.children) {
return false; return false;
} }
childrenul = this.Y.Node.create('<ul></ul>'); childrenul = this.Y.Node.create('<ul></ul>');
@@ -194,9 +194,9 @@ M.block_navigation.classes.tree.prototype.add_branch = function(branchxml, targe
childrenul = branch.inject_into_dom(target); childrenul = branch.inject_into_dom(target);
} }
if (childrenul) { if (childrenul) {
for (var i=0;i<branch.children.childNodes.length;i++) { for (i in branch.children) {
// Add each branch to the tree // Add each branch to the tree
this.add_branch(branch.children.childNodes[i], childrenul, depth+1); this.add_branch(branch.children[i], childrenul, depth+1);
} }
} }
return true; return true;
@@ -223,9 +223,9 @@ M.block_navigation.classes.tree.prototype.toggleexpansion = function(e) {
* @class branch * @class branch
* @constructor * @constructor
* @param {M.block_navigation.classes.tree} tree * @param {M.block_navigation.classes.tree} tree
* @param {xmldoc|null} xml * @param {object|null} obj
*/ */
M.block_navigation.classes.branch = function(tree, xml) { M.block_navigation.classes.branch = function(tree, obj) {
this.tree = tree; this.tree = tree;
this.name = null; this.name = null;
this.title = null; this.title = null;
@@ -240,47 +240,23 @@ M.block_navigation.classes.branch = function(tree, xml) {
this.hidden = false; this.hidden = false;
this.haschildren = false; this.haschildren = false;
this.children = false; this.children = false;
if (xml !== null) { if (obj !== null) {
// Construct from the provided xml // Construct from the provided xml
this.construct_from_xml(xml); this.construct_from_json(obj);
} }
} }
/** M.block_navigation.classes.branch.prototype.construct_from_json = function(obj) {
* Constructs a branch from XML for (var i in obj) {
* @param {xmldoc} xml this[i] = obj[i];
*/ }
M.block_navigation.classes.branch.prototype.construct_from_xml = function(xml) { if (this.children) {
// Get required attributes this.haschildren = true;
this.title = xml.getAttribute('title'); }
this.classname = xml.getAttribute('class');
this.id = xml.getAttribute('id');
this.link = xml.getAttribute('link');
this.icon = xml.getAttribute('icon');
this.key = xml.getAttribute('key');
this.type = xml.getAttribute('type');
this.expandable = xml.getAttribute('expandable');
this.expansionceiling = xml.getAttribute('expansionceiling');
// Boolean attributes
this.hidden = (xml.getAttribute('hidden')=='true');
this.haschildren = (xml.getAttribute('haschildren')=='true');
if (this.id && this.id.match(/^expandable_branch_\d+$/)) { if (this.id && this.id.match(/^expandable_branch_\d+$/)) {
// Assign a new unique id for this new expandable branch // Assign a new unique id for this new expandable branch
M.block_navigation.expandablebranchcount++; M.block_navigation.expandablebranchcount++;
this.id = 'expandable_branch_'+M.block_navigation.expandablebranchcount; this.id = 'expandable_branch_'+M.block_navigation.expandablebranchcount;
} }
// Retrieve any additional information
for (var i=0; i<xml.childNodes.length;i++) {
var node = xml.childNodes[i];
switch (node.nodeName.toLowerCase()) {
case 'name':
this.name = node.firstChild.nodeValue;
break;
case 'children':
this.children = node;
}
}
} }
/** /**
* Injects a branch into the tree at the given location * Injects a branch into the tree at the given location

View File

@@ -81,7 +81,7 @@ class block_settings_navigation_tree extends block_tree {
global $CFG; global $CFG;
$this->_initialise_dock(); $this->_initialise_dock();
$this->page->requires->js_module(array('name'=>'core_dock', 'fullpath'=>'/blocks/dock.js', 'requires'=>array('base', 'cookie', 'dom', 'io', 'node', 'event-custom', 'event-mouseenter', 'yui2-container'))); $this->page->requires->js_module(array('name'=>'core_dock', 'fullpath'=>'/blocks/dock.js', 'requires'=>array('base', 'cookie', 'dom', 'io', 'node', 'event-custom', 'event-mouseenter', 'yui2-container')));
$module = array('name'=>'block_navigation', 'fullpath'=>'/blocks/global_navigation_tree/navigation.js', 'requires'=>array('core_dock', 'io', 'node', 'dom', 'event-custom')); $module = array('name'=>'block_navigation', 'fullpath'=>'/blocks/global_navigation_tree/navigation.js', 'requires'=>array('core_dock', 'io', 'node', 'dom', 'event-custom', 'json-parse'));
$arguments = array($this->instance->id, array('instance'=>$this->instance->id, 'candock'=>$this->instance_can_be_docked())); $arguments = array($this->instance->id, array('instance'=>$this->instance->id, 'candock'=>$this->instance_can_be_docked()));
$this->page->requires->js_init_call('M.block_navigation.init_add_tree', $arguments, false, $module); $this->page->requires->js_init_call('M.block_navigation.init_add_tree', $arguments, false, $module);
user_preference_allow_ajax_update('docked_block_instance_'.$this->instance->id, PARAM_INT); user_preference_allow_ajax_update('docked_block_instance_'.$this->instance->id, PARAM_INT);

View File

@@ -29,58 +29,61 @@
require_once(dirname(__FILE__) . '/../../config.php'); require_once(dirname(__FILE__) . '/../../config.php');
/** Include course lib for its functions */ /** Include course lib for its functions */
require_once($CFG->dirroot.'/course/lib.php'); require_once($CFG->dirroot.'/course/lib.php');
// Start buffer capture so that we can `remove` any errors
ob_start();
// Require elementid (this identifies which HTML element it is being expanded try {
$elementid = optional_param('elementid', null, PARAM_INT); // Start buffer capture so that we can `remove` any errors
// Require id This is the key for whatever branch we want to get ob_start();
$branchid = optional_param('id', null, PARAM_INT); // Require id This is the key for whatever branch we want to get
// This identifies the type of the branch we want to get $branchid = required_param('id', PARAM_INT);
$branchtype = optional_param('type', null, PARAM_INT); // This identifies the type of the branch we want to get
// This identifies the block instance requesting AJAX extension $branchtype = required_param('type', PARAM_INT);
$instanceid = optional_param('instance', null, PARAM_INT); // This identifies the block instance requesting AJAX extension
$instanceid = optional_param('instance', null, PARAM_INT);
// Create a global nav object // Create a global nav object
$navigation = new limited_global_navigation(); $navigation = new global_navigation_for_ajax();
// If set to true then we need to call toggle display
$toggledisplay = false; // If set to true then we need to call toggle display
if ($instanceid!==null) { $toggledisplay = false;
// Get the db record for the block instance if ($instanceid!==null) {
$blockrecords = $DB->get_record('block_instances', array('id'=>$instanceid,'blockname'=>'global_navigation_tree')); // Get the db record for the block instance
if ($blockrecords!=false) { $blockrecords = $DB->get_record('block_instances', array('id'=>$instanceid,'blockname'=>'global_navigation_tree'));
// Instantiate a block_instance object so we can access congif if ($blockrecords!=false) {
$block = block_instance('global_navigation_tree', $blockrecords); // Instantiate a block_instance object so we can access congif
// Check if the expansion limit config option has been set and isn't the default [everything] $block = block_instance('global_navigation_tree', $blockrecords);
if (!empty($block->config->expansionlimit) && $block->config->expansionlimit > '0') { // Check if the expansion limit config option has been set and isn't the default [everything]
// Set the expansion limit if (!empty($block->config->expansionlimit) && $block->config->expansionlimit > '0') {
$navigation->expansionlimit = $block->config->expansionlimit; // Set the expansion limit
$toggledisplay = true; $navigation->expansionlimit = $block->config->expansionlimit;
} $toggledisplay = true;
if (empty($block->config->showemptybranches) || $block->config->showemptybranches=='no') { }
$navigation->showemptybranches = false; if (empty($block->config->showemptybranches) || $block->config->showemptybranches=='no') {
$navigation->showemptybranches = false;
}
} }
} }
// Create a navigation object to use, we can't guarantee PAGE will be complete
$expandable = $navigation->initialise($branchtype, $branchid);
$converter = new navigation_json();
if ($toggledisplay) {
// Toggle display of item types we dont' want to display
$navigation->toggle_type_display($navigation->expansionlimit);
$converter->set_expansionceiling($navigation->expansionlimit);
}
// Find the actuall branch we are looking for
$branch = $navigation->find_child($branchid, $branchtype);
// Stop buffering errors at this point
$html = ob_get_contents();
ob_end_clean();
} catch (Exception $e) {
die('Error: '.$e->message);
} }
// Create a navigation object to use, we can't guarantee PAGE will be complete
$expandable = $navigation->initialise($branchtype, $branchid);
$converter = new navigation_xml();
if ($toggledisplay) {
// Toggle display of item types we dont' want to display
$navigation->toggle_type_display($navigation->expansionlimit);
$converter->set_expansionceiling($navigation->expansionlimit);
}
// Find the actuall branch we are looking for
$branch = $navigation->find_child($branchid, $branchtype);
// Stop buffering errors at this point
$html = ob_get_contents();
ob_end_clean();
// Check if the buffer contianed anything if it did ERROR! // Check if the buffer contianed anything if it did ERROR!
if (trim($html)!=='') { if (trim($html) !== '') {
die('Errors were encountered while producing the navigation branch'."\n\n\n".$html); die('Errors were encountered while producing the navigation branch'."\n\n\n".$html);
} }
// Check that branch isn't empty... if it is ERROR! // Check that branch isn't empty... if it is ERROR!
@@ -91,7 +94,6 @@ if (empty($branch) || $branch->nodetype !== navigation_node::NODETYPE_BRANCH) {
// Prepare an XML converter for the branch // Prepare an XML converter for the branch
$converter->set_expandable($expandable); $converter->set_expandable($expandable);
// Set XML headers // Set XML headers
header('Content-type: text/xml'); header('Content-type: text/json');
echo '<?xml version="1.0" encoding="utf-8"?>';
// Convert and output the branch as XML // Convert and output the branch as XML
echo $converter->convert($branch); echo $converter->convert($branch);

View File

@@ -52,7 +52,7 @@ define('NAVIGATION_CACHE_NAME', 'navigation');
*/ */
class navigation_node { class navigation_node {
/** Used to identify this node a leaf (default) */ /** Used to identify this node a leaf (default) */
const NODETYPE_LEAF = 0; const NODETYPE_LEAF = 0;
/** Used to identify this node a branch, happens with children */ /** Used to identify this node a branch, happens with children */
const NODETYPE_BRANCH = 1; const NODETYPE_BRANCH = 1;
/** Unknown node type */ /** Unknown node type */
@@ -64,7 +64,7 @@ class navigation_node {
/** Course node type */ /** Course node type */
const TYPE_COURSE = 20; const TYPE_COURSE = 20;
/** Course Structure node type */ /** Course Structure node type */
const TYPE_SECTION = 30; const TYPE_SECTION = 30;
/** Activity node type, e.g. Forum, Quiz */ /** Activity node type, e.g. Forum, Quiz */
const TYPE_ACTIVITY = 40; const TYPE_ACTIVITY = 40;
/** Resource node type, e.g. Link to a file, or label */ /** Resource node type, e.g. Link to a file, or label */
@@ -74,7 +74,7 @@ class navigation_node {
/** Setting node type, used only within settings nav */ /** Setting node type, used only within settings nav */
const TYPE_SETTING = 70; const TYPE_SETTING = 70;
/** Setting node type, used only within settings nav */ /** Setting node type, used only within settings nav */
const TYPE_USER = 80; const TYPE_USER = 80;
/** @var int Parameter to aid the coder in tracking [optional] */ /** @var int Parameter to aid the coder in tracking [optional] */
public $id = null; public $id = null;
@@ -154,7 +154,6 @@ class navigation_node {
* @param string|array $properties * @param string|array $properties
*/ */
public function __construct($properties) { public function __construct($properties) {
global $PAGE;
if (is_array($properties)) { if (is_array($properties)) {
if (array_key_exists('text', $properties)) { if (array_key_exists('text', $properties)) {
$this->text = $properties['text']; $this->text = $properties['text'];
@@ -227,11 +226,10 @@ class navigation_node {
global $FULLME, $PAGE; global $FULLME, $PAGE;
if (self::$fullmeurl == null) { if (self::$fullmeurl == null) {
if ($PAGE->has_set_url()) { if ($PAGE->has_set_url()) {
$url = new moodle_url($PAGE->url); $this->override_active_url(new moodle_url($PAGE->url));
} else { } else {
$url = new moodle_url($FULLME); $this->override_active_url(new moodle_url($FULLME));
} }
self::$fullmeurl = $url;
} }
if ($this->action instanceof moodle_url && $this->action->compare(self::$fullmeurl, $strength)) { if ($this->action instanceof moodle_url && $this->action->compare(self::$fullmeurl, $strength)) {
@@ -397,7 +395,7 @@ class navigation_node {
* @return string The HTML content * @return string The HTML content
*/ */
public function content($shorttext=false) { public function content($shorttext=false) {
global $OUTPUT, $CFG, $PAGE; global $OUTPUT;
if (!$this->display) { if (!$this->display) {
return ''; return '';
} }
@@ -415,7 +413,7 @@ class navigation_node {
$icon = $OUTPUT->render($this->icon); $icon = $OUTPUT->render($this->icon);
$content = $icon.$content; // use CSS for spacing of icons $content = $icon.$content; // use CSS for spacing of icons
} else if ($this->helpbutton !== null) { } else if ($this->helpbutton !== null) {
$content = sprintf('%s<span class="clearhelpbutton">%s</span>', trim($this->helpbutton), $content); $content = trim($this->helpbutton).html_writer::tag('span', array('class'=>'clearhelpbutton'), $content);
} }
if ($content === '') { if ($content === '') {
@@ -875,7 +873,7 @@ class global_navigation extends navigation_node {
* Sets up the object with basic settings and preparse it for use * Sets up the object with basic settings and preparse it for use
*/ */
public function __construct() { public function __construct() {
global $CFG, $PAGE; global $CFG;
if (during_initial_install()) { if (during_initial_install()) {
return false; return false;
} }
@@ -918,13 +916,13 @@ class global_navigation extends navigation_node {
* @return bool Returns true * @return bool Returns true
*/ */
public function initialise($jsargs = null) { public function initialise($jsargs = null) {
global $PAGE, $SITE, $CFG, $USER; global $PAGE, $SITE, $USER;
if ($this->initialised || during_initial_install()) { if ($this->initialised || during_initial_install()) {
return true; return true;
} }
$start = microtime(false); $start = microtime(false);
$this->depthforward = 1; $this->depthforward = 1;
$this->context = &$PAGE->context; $this->context = $PAGE->context;
$contextlevel = $this->context->contextlevel; $contextlevel = $this->context->contextlevel;
if ($contextlevel == CONTEXT_COURSE && $PAGE->course->id==$SITE->id) { if ($contextlevel == CONTEXT_COURSE && $PAGE->course->id==$SITE->id) {
$contextlevel = 10; $contextlevel = 10;
@@ -973,9 +971,8 @@ class global_navigation extends navigation_node {
$this->collapse_at_depth($this->depthforward+$depth); $this->collapse_at_depth($this->depthforward+$depth);
$this->respect_forced_open(); $this->respect_forced_open();
$expandable = array(); $this->expandable = array();
$this->find_expandable($expandable); $this->find_expandable($this->expandable);
$this->expandable = $expandable;
$this->initialised = true; $this->initialised = true;
return true; return true;
} }
@@ -1002,8 +999,7 @@ class global_navigation extends navigation_node {
$user = $DB->get_record('user', array('id'=>(int)$user), '*', MUST_EXIST); $user = $DB->get_record('user', array('id'=>(int)$user), '*', MUST_EXIST);
} }
$baseargs = array('id'=>$user->id); $baseargs = array('id'=>$user->id);
$contexts = new stdClass; $usercontext = get_context_instance(CONTEXT_USER, $user->id);
$contexts->user = get_context_instance(CONTEXT_USER, $user->id);
// Get the course set against the page, by default this will be the site // Get the course set against the page, by default this will be the site
$course = $PAGE->course; $course = $PAGE->course;
@@ -1021,13 +1017,13 @@ class global_navigation extends navigation_node {
$coursetab = $this->find_child($course->id, self::TYPE_COURSE); $coursetab = $this->find_child($course->id, self::TYPE_COURSE);
} }
// Get the context for the course // Get the context for the course
$contexts->course = get_context_instance(CONTEXT_COURSE, $course->id); $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
$baseargs['course'] = $course->id; $baseargs['course'] = $course->id;
$issitecourse = false; $issitecourse = false;
} else { } else {
// Load all categories and get the context for the system // Load all categories and get the context for the system
$this->load_categories(); $this->load_categories();
$contexts->course = get_context_instance(CONTEXT_SYSTEM); $coursecontext = get_context_instance(CONTEXT_SYSTEM);
$issitecourse = true; $issitecourse = true;
} }
@@ -1055,13 +1051,13 @@ class global_navigation extends navigation_node {
// If the user is the current user or has permission to view the details of the requested // If the user is the current user or has permission to view the details of the requested
// user than add a view profile link. // user than add a view profile link.
if ($iscurrentuser || has_capability('moodle/user:viewdetails', $contexts->course) || has_capability('moodle/user:viewdetails', $contexts->user)) { if ($iscurrentuser || has_capability('moodle/user:viewdetails', $coursecontext) || has_capability('moodle/user:viewdetails', $usercontext)) {
$usernode->add(get_string('viewprofile'), new moodle_url('/user/view.php',$baseargs)); $usernode->add(get_string('viewprofile'), new moodle_url('/user/view.php',$baseargs));
} }
// Add nodes for forum posts and discussions if the user can view either or both // Add nodes for forum posts and discussions if the user can view either or both
$canviewposts = has_capability('moodle/user:readuserposts', $contexts->user); $canviewposts = has_capability('moodle/user:readuserposts', $usercontext);
$canviewdiscussions = has_capability('mod/forum:viewdiscussion', $contexts->course); $canviewdiscussions = has_capability('mod/forum:viewdiscussion', $coursecontext);
if ($canviewposts || $canviewdiscussions) { if ($canviewposts || $canviewdiscussions) {
$forumtab = $usernode->add(get_string('forumposts', 'forum')); $forumtab = $usernode->add(get_string('forumposts', 'forum'));
$forumtab = $usernode->get($forumtab); $forumtab = $usernode->get($forumtab);
@@ -1074,14 +1070,14 @@ class global_navigation extends navigation_node {
} }
// Add a node to view the users notes if permitted // Add a node to view the users notes if permitted
if (!empty($CFG->enablenotes) && has_any_capability(array('moodle/notes:manage', 'moodle/notes:view'), $contexts->course)) { if (!empty($CFG->enablenotes) && has_any_capability(array('moodle/notes:manage', 'moodle/notes:view'), $coursecontext)) {
$usernode->add(get_string('notes', 'notes'), new moodle_url('/notes/index.php',array('user'=>$user->id, 'course'=>$contexts->course->instanceid))); $usernode->add(get_string('notes', 'notes'), new moodle_url('/notes/index.php',array('user'=>$user->id, 'course'=>$coursecontext->instanceid)));
} }
// Add a reports tab and then add reports the the user has permission to see. // Add a reports tab and then add reports the the user has permission to see.
$reporttab = $usernode->add(get_string('activityreports')); $reporttab = $usernode->add(get_string('activityreports'));
$reporttab = $usernode->get($reporttab); $reporttab = $usernode->get($reporttab);
$anyreport = has_capability('moodle/user:viewuseractivitiesreport', $contexts->user); $anyreport = has_capability('moodle/user:viewuseractivitiesreport', $usercontext);
$viewreports = ($anyreport || ($course->showreports && $iscurrentuser)); $viewreports = ($anyreport || ($course->showreports && $iscurrentuser));
$reportargs = array('user'=>$user->id); $reportargs = array('user'=>$user->id);
if (!empty($course->id)) { if (!empty($course->id)) {
@@ -1089,34 +1085,34 @@ class global_navigation extends navigation_node {
} else { } else {
$reportargs['id'] = SITEID; $reportargs['id'] = SITEID;
} }
if ($viewreports || has_capability('coursereport/outline:view', $contexts->course)) { if ($viewreports || has_capability('coursereport/outline:view', $coursecontext)) {
$reporttab->add(get_string('outlinereport'), new moodle_url('/course/user.php', array_merge($reportargs, array('mode'=>'outline')))); $reporttab->add(get_string('outlinereport'), new moodle_url('/course/user.php', array_merge($reportargs, array('mode'=>'outline'))));
$reporttab->add(get_string('completereport'), new moodle_url('/course/user.php', array_merge($reportargs, array('mode'=>'complete')))); $reporttab->add(get_string('completereport'), new moodle_url('/course/user.php', array_merge($reportargs, array('mode'=>'complete'))));
} }
if ($viewreports || has_capability('coursereport/log:viewtoday', $contexts->course)) { if ($viewreports || has_capability('coursereport/log:viewtoday', $coursecontext)) {
$reporttab->add(get_string('todaylogs'), new moodle_url('/course/user.php', array_merge($reportargs, array('mode'=>'todaylogs')))); $reporttab->add(get_string('todaylogs'), new moodle_url('/course/user.php', array_merge($reportargs, array('mode'=>'todaylogs'))));
} }
if ($viewreports || has_capability('coursereport/log:view', $contexts->course)) { if ($viewreports || has_capability('coursereport/log:view', $coursecontext)) {
$reporttab->add(get_string('alllogs'), new moodle_url('/course/user.php', array_merge($reportargs, array('mode'=>'alllogs')))); $reporttab->add(get_string('alllogs'), new moodle_url('/course/user.php', array_merge($reportargs, array('mode'=>'alllogs'))));
} }
if (!empty($CFG->enablestats)) { if (!empty($CFG->enablestats)) {
if ($viewreports || has_capability('coursereport/stats:view', $contexts->course)) { if ($viewreports || has_capability('coursereport/stats:view', $coursecontext)) {
$reporttab->add(get_string('stats'), new moodle_url('/course/user.php', array_merge($reportargs, array('mode'=>'stats')))); $reporttab->add(get_string('stats'), new moodle_url('/course/user.php', array_merge($reportargs, array('mode'=>'stats'))));
} }
} }
$gradeaccess = false; $gradeaccess = false;
if (has_capability('moodle/grade:viewall', $contexts->course)) { if (has_capability('moodle/grade:viewall', $coursecontext)) {
//ok - can view all course grades //ok - can view all course grades
$gradeaccess = true; $gradeaccess = true;
} else if ($course->showgrades) { } else if ($course->showgrades) {
if ($iscurrentuser && has_capability('moodle/grade:view', $contexts->course)) { if ($iscurrentuser && has_capability('moodle/grade:view', $coursecontext)) {
//ok - can view own grades //ok - can view own grades
$gradeaccess = true; $gradeaccess = true;
} else if (has_capability('moodle/grade:viewall', $contexts->user)) { } else if (has_capability('moodle/grade:viewall', $usercontext)) {
// ok - can view grades of this user - parent most probably // ok - can view grades of this user - parent most probably
$gradeaccess = true; $gradeaccess = true;
} else if ($anyreport) { } else if ($anyreport) {
@@ -1137,9 +1133,9 @@ class global_navigation extends navigation_node {
// If the user is the current user add the repositories for the current user // If the user is the current user add the repositories for the current user
if ($iscurrentuser) { if ($iscurrentuser) {
require_once($CFG->dirroot . '/repository/lib.php'); require_once($CFG->dirroot . '/repository/lib.php');
$editabletypes = repository::get_editable_types($contexts->user); $editabletypes = repository::get_editable_types($usercontext);
if (!empty($editabletypes)) { if (!empty($editabletypes)) {
$usernode->add(get_string('repositories', 'repository'), new moodle_url('/repository/manage_instances.php', array('contextid', $contexts->user->id))); $usernode->add(get_string('repositories', 'repository'), new moodle_url('/repository/manage_instances.php', array('contextid', $usercontext->id)));
} }
} }
return true; return true;
@@ -1218,8 +1214,6 @@ class global_navigation extends navigation_node {
*/ */
protected function format_display_course_content($format, $default=true) { protected function format_display_course_content($format, $default=true) {
global $CFG; global $CFG;
//
//
$formatlib = $CFG->dirroot.'/course/format/'.$format.'/lib.php'; $formatlib = $CFG->dirroot.'/course/format/'.$format.'/lib.php';
if (file_exists($formatlib)) { if (file_exists($formatlib)) {
require_once($formatlib); require_once($formatlib);
@@ -1238,7 +1232,7 @@ class global_navigation extends navigation_node {
* @param array $keys By reference * @param array $keys By reference
*/ */
protected function load_course_activities(&$keys, $course=null) { protected function load_course_activities(&$keys, $course=null) {
global $PAGE, $OUTPUT, $CFG, $FULLME; global $PAGE, $CFG, $FULLME;
if ($course === null) { if ($course === null) {
$course = $PAGE->course; $course = $PAGE->course;
@@ -1269,13 +1263,11 @@ class global_navigation extends navigation_node {
$icon = new pix_icon('icon', '', $module->modname); $icon = new pix_icon('icon', '', $module->modname);
} }
$url = new moodle_url('/mod/'.$module->modname.'/view.php', array('id'=>$module->id)); $url = new moodle_url('/mod/'.$module->modname.'/view.php', array('id'=>$module->id));
$type = navigation_node::TYPE_ACTIVITY; // TODO: add some new fast test for resource types $this->add_to_path($keys, $module->id, $module->name, $module->name, self::TYPE_ACTIVITY, $url, $icon);
$child = $this->find_child($module->id, self::TYPE_ACTIVITY);
$this->add_to_path($keys, $module->id, $module->name, $module->name, $type, $url, $icon);
$child = $this->find_child($module->id, $type);
if ($child != false) { if ($child != false) {
$child->title(get_string('modulename', $module->modname)); $child->title(get_string('modulename', $module->modname));
if ($type==navigation_node::TYPE_ACTIVITY && $this->module_extends_navigation($module->modname)) { if ($this->module_extends_navigation($module->modname)) {
$child->nodetype = self::NODETYPE_BRANCH; $child->nodetype = self::NODETYPE_BRANCH;
} }
if (!$module->visible) { if (!$module->visible) {
@@ -1290,7 +1282,7 @@ class global_navigation extends navigation_node {
* @param array $keys By reference * @param array $keys By reference
*/ */
protected function load_section_activities(&$keys, $singlesectionid=false, $course=null) { protected function load_section_activities(&$keys, $singlesectionid=false, $course=null) {
global $PAGE, $OUTPUT, $CFG, $FULLME; global $PAGE, $CFG, $FULLME;
if ($course === null) { if ($course === null) {
$course = $PAGE->course; $course = $PAGE->course;
@@ -1326,20 +1318,19 @@ class global_navigation extends navigation_node {
$icon = new pix_icon('icon', '', $module->modname); $icon = new pix_icon('icon', '', $module->modname);
} }
$url = new moodle_url('/mod/'.$module->modname.'/view.php', array('id'=>$module->id)); $url = new moodle_url('/mod/'.$module->modname.'/view.php', array('id'=>$module->id));
$type = navigation_node::TYPE_ACTIVITY; // TODO: add some new fast test for resource types
$path = $keys; $path = $keys;
if ($course->id !== SITEID) { if ($course->id !== SITEID) {
$path[] = $sections[$module->sectionnum]->id; $path[] = $sections[$module->sectionnum]->id;
} }
$this->add_to_path($path, $module->id, $module->name, $module->name, $type, $url, $icon); $this->add_to_path($path, $module->id, $module->name, $module->name, navigation_node::TYPE_ACTIVITY, $url, $icon);
$child = $this->find_child($module->id, $type); $child = $this->find_child($module->id, navigation_node::TYPE_ACTIVITY);
if ($child != false) { if ($child != false) {
$child->title(get_string('modulename', $module->modname)); $child->title(get_string('modulename', $module->modname));
if (!$module->visible) { if (!$module->visible) {
$child->hidden = true; $child->hidden = true;
} }
if ($type==navigation_node::TYPE_ACTIVITY && $this->module_extends_navigation($module->modname)) { if ($this->module_extends_navigation($module->modname)) {
$child->nodetype = self::NODETYPE_BRANCH; $child->nodetype = self::NODETYPE_BRANCH;
} }
} }
@@ -1410,7 +1401,7 @@ class global_navigation extends navigation_node {
* @return void * @return void
*/ */
protected function load_activity($keys) { protected function load_activity($keys) {
global $DB, $CFG, $PAGE; global $CFG, $PAGE;
if (!$PAGE->cm && $this->context->contextlevel == CONTEXT_MODULE && $this->context->instanceid) { if (!$PAGE->cm && $this->context->contextlevel == CONTEXT_MODULE && $this->context->instanceid) {
// This is risky but we have no other choice... we need that module and the module // This is risky but we have no other choice... we need that module and the module
@@ -1426,13 +1417,6 @@ class global_navigation extends navigation_node {
} }
} }
$module = $DB->get_record('modules', array('id'=>$PAGE->cm->module));
if (!$module) {
debugging('Invalid Module ID picked up while attempting to load the activity for the navigation', DEBUG_DEVELOPER);
return;
}
$node = $this->find_child($PAGE->cm->id, self::TYPE_ACTIVITY); $node = $this->find_child($PAGE->cm->id, self::TYPE_ACTIVITY);
if ($node) { if ($node) {
$node->make_active(); $node->make_active();
@@ -1442,8 +1426,8 @@ class global_navigation extends navigation_node {
} else { } else {
$this->context = $PAGE->course->context; $this->context = $PAGE->course->context;
} }
$file = $CFG->dirroot.'/mod/'.$module->name.'/lib.php'; $file = $CFG->dirroot.'/mod/'.$PAGE->activityname.'/lib.php';
$function = $module->name.'_extend_navigation'; $function = $PAGE->activityname.'_extend_navigation';
if (empty($PAGE->cm->context)) { if (empty($PAGE->cm->context)) {
$PAGE->cm->context = get_context_instance(CONTEXT_MODULE, $PAGE->cm->instance); $PAGE->cm->context = get_context_instance(CONTEXT_MODULE, $PAGE->cm->instance);
@@ -1452,7 +1436,7 @@ class global_navigation extends navigation_node {
if (file_exists($file)) { if (file_exists($file)) {
require_once($file); require_once($file);
if (function_exists($function)) { if (function_exists($function)) {
$function($node, $PAGE->course, $module, $PAGE->cm); $function($node, $PAGE->course, $PAGE->activityrecord, $PAGE->cm);
} }
} }
} }
@@ -1467,7 +1451,6 @@ class global_navigation extends navigation_node {
* we need to * we need to
*/ */
protected function add_categories(&$keys, $categories, $depth=0) { protected function add_categories(&$keys, $categories, $depth=0) {
global $CFG;
if (is_array($categories) && count($categories)>0) { if (is_array($categories) && count($categories)>0) {
foreach ($categories as $category) { foreach ($categories as $category) {
$url = new moodle_url('/course/category.php', array('id'=>$category->id, 'categoryedit'=>'on', 'sesskey'=>sesskey())); $url = new moodle_url('/course/category.php', array('id'=>$category->id, 'categoryedit'=>'on', 'sesskey'=>sesskey()));
@@ -1485,7 +1468,6 @@ class global_navigation extends navigation_node {
* @param stdClass $category * @param stdClass $category
*/ */
protected function add_category_by_path($category) { protected function add_category_by_path($category) {
global $CFG;
$url = new moodle_url('/course/category.php', array('id'=>$category->id, 'categoryedit'=>'on', 'sesskey'=>sesskey())); $url = new moodle_url('/course/category.php', array('id'=>$category->id, 'categoryedit'=>'on', 'sesskey'=>sesskey()));
$keys = explode('/',trim($category->path,'/ ')); $keys = explode('/',trim($category->path,'/ '));
// Check this category hadsn't already been added // Check this category hadsn't already been added
@@ -1507,7 +1489,7 @@ class global_navigation extends navigation_node {
* @return bool * @return bool
*/ */
public function add_courses($courses, $categoryid=null) { public function add_courses($courses, $categoryid=null) {
global $CFG, $OUTPUT, $SITE; global $SITE;
if (is_array($courses) && count($courses)>0) { if (is_array($courses) && count($courses)>0) {
// Work out if the user can view hidden courses, just incase // Work out if the user can view hidden courses, just incase
if (!$this->cache->cached('canviewhiddencourses')) { if (!$this->cache->cached('canviewhiddencourses')) {
@@ -1560,7 +1542,7 @@ class global_navigation extends navigation_node {
* @return bool * @return bool
*/ */
protected function load_course(&$keys, $course=null) { protected function load_course(&$keys, $course=null) {
global $PAGE, $CFG, $OUTPUT; global $PAGE, $CFG;
if ($course===null) { if ($course===null) {
$course = $PAGE->course; $course = $PAGE->course;
} }
@@ -1677,7 +1659,7 @@ class global_navigation extends navigation_node {
* @return bool * @return bool
*/ */
public function add_course_section_generic(&$keys, $course=null, $name=null, $activeparam = null) { public function add_course_section_generic(&$keys, $course=null, $name=null, $activeparam = null) {
global $PAGE, $CFG, $OUTPUT; global $PAGE;
if ($course === null) { if ($course === null) {
$course = $PAGE->course; $course = $PAGE->course;
@@ -1724,8 +1706,6 @@ class global_navigation extends navigation_node {
} }
$viewhiddensections = $this->cache->canviewhiddensections; $viewhiddensections = $this->cache->canviewhiddensections;
$selectedstructure = optional_param($activeparam,false,PARAM_INT);
// MDL-20242 + MDL-21564 // MDL-20242 + MDL-21564
$sections = array_slice($sections, 0, $course->numsections+1, true); $sections = array_slice($sections, 0, $course->numsections+1, true);
@@ -1771,7 +1751,7 @@ class global_navigation extends navigation_node {
* @return int The number of categories * @return int The number of categories
*/ */
protected function load_course_categories(&$keys) { protected function load_course_categories(&$keys) {
global $PAGE, $CFG, $DB; global $PAGE;
$categories = $PAGE->categories; $categories = $PAGE->categories;
if (is_array($categories) && count($categories)>0) { if (is_array($categories) && count($categories)>0) {
$categories = array_reverse($categories); $categories = array_reverse($categories);
@@ -1795,7 +1775,7 @@ class global_navigation extends navigation_node {
* @return int The depth of categories that were loaded * @return int The depth of categories that were loaded
*/ */
protected function load_categories($categoryid=0) { protected function load_categories($categoryid=0) {
global $PAGE, $CFG, $DB, $USER; global $CFG, $DB, $USER;
if ($categoryid === 0 && $this->allcategoriesloaded) { if ($categoryid === 0 && $this->allcategoriesloaded) {
return 0; return 0;
@@ -1873,19 +1853,6 @@ class global_navigation extends navigation_node {
$this->add_courses($courses); $this->add_courses($courses);
} else if ($categoryid === 0) { } else if ($categoryid === 0) {
$keys = array(); $keys = array();
if ($categoryid!=0) {
if (!$this->cache->cached('category'.$categoryid)) {
$this->cache->{'category'.$categoryid} = $DB->get_record('course_categories', array('id' => $categoryid), 'id,name,path');
}
$category = $this->cache->{'category'.$categoryid};
if ($category!=false) {
$keys = explode('/',trim($category->path,'/ '));
$categories = $DB->get_records_select('course_categories', 'id IN ('.join(',', $keys).')', array(), 'path ASC, sortorder ASC');
foreach ($categories as $category) {
$this->add_category_by_path($category);
}
}
}
$categories = $DB->get_records('course_categories', array('parent' => $categoryid), 'sortorder ASC'); $categories = $DB->get_records('course_categories', array('parent' => $categoryid), 'sortorder ASC');
$this->add_categories($keys, $categories); $this->add_categories($keys, $categories);
$this->add_courses($courses, $categoryid); $this->add_courses($courses, $categoryid);
@@ -1945,7 +1912,7 @@ class global_navigation extends navigation_node {
foreach ($categories as $category) { foreach ($categories as $category) {
$this->children = array_merge($this->children, $category->children); $this->children = array_merge($this->children, $category->children);
$this->remove_child($category->key, self::TYPE_CATEGORY); $this->remove_child($category->key, self::TYPE_CATEGORY);
} }
$categories = $this->get_children_by_type(self::TYPE_CATEGORY); $categories = $this->get_children_by_type(self::TYPE_CATEGORY);
} }
} }
@@ -1967,7 +1934,7 @@ class global_navigation extends navigation_node {
* @copyright 2009 Sam Hemelryk * @copyright 2009 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/ */
class limited_global_navigation extends global_navigation { class global_navigation_for_ajax extends global_navigation {
/** /**
* Initialise the limited navigation object, calling it to auto generate * Initialise the limited navigation object, calling it to auto generate
* *
@@ -1976,36 +1943,54 @@ class limited_global_navigation extends global_navigation {
* This is used when the global navigation is being generated without other fully * This is used when the global navigation is being generated without other fully
* initialised Moodle objects * initialised Moodle objects
* *
* @param int $type What to load for e.g. TYPE_SYSTEM * @param int $branchtype What to load for e.g. TYPE_SYSTEM
* @param int $instanceid The instance id for what ever is being loaded * @param int $id The instance id for what ever is being loaded
* @return array An array of nodes that are expandable by AJAX * @return array An array of nodes that are expandable by AJAX
*/ */
public function initialise($type, $instanceid) { public function initialise($branchtype, $id) {
global $DB;
if ($this->initialised || during_initial_install()) { if ($this->initialised || during_initial_install()) {
return $this->expandable; return $this->expandable;
} }
$depth = 0;
switch ($type) { // Branchtype will be one of navigation_node::TYPE_*
case self::TYPE_CATEGORY: switch ($branchtype) {
$depth = $this->load_category($instanceid); case self::TYPE_CATEGORY :
require_login();
$depth = $this->load_category($id);
break; break;
case self::TYPE_COURSE: case self::TYPE_COURSE :
$depth = $this->load_course($instanceid); $course = $DB->get_record('course', array('id' => $id), '*', MUST_EXIST);
require_course_login($course);
$depth = $this->load_course($id);
break; break;
case self::TYPE_SECTION: case self::TYPE_SECTION :
$depth = $this->load_section($instanceid); $sql = 'SELECT c.*
FROM {course} c
LEFT JOIN {course_sections} cs ON cs.course = c.id
WHERE cs.id = ?';
$course = $DB->get_record_sql($sql, array($id), MUST_EXIST);
require_course_login($course);
$depth = $this->load_section($id);
break; break;
case self::TYPE_ACTIVITY: case self::TYPE_ACTIVITY :
$depth = $this->load_activity($instanceid); $cm = get_coursemodule_from_id(false, $id, 0, false, MUST_EXIST);
$course = $DB->get_record('course', array('id'=>$cm->course), '*', MUST_EXIST);
require_course_login($course, true, $cm);
$depth = $this->load_activity($id);
break;
default:
return $this->expandable;
break; break;
} }
$this->collapse_at_depth($this->depthforward+$depth); $this->collapse_at_depth($this->depthforward+$depth);
$this->respect_forced_open(); $this->respect_forced_open();
$expandable = array(); $this->expandable = array();
$this->find_expandable($expandable); $this->find_expandable($this->expandable);
$this->expandable = $expandable;
$this->initialised = true; $this->initialised = true;
return $expandable; return $this->expandable;
} }
/** /**
@@ -2031,15 +2016,14 @@ class limited_global_navigation extends global_navigation {
global $DB, $PAGE; global $DB, $PAGE;
if (!$this->cache->cached('course'.$instanceid)) { if (!$this->cache->cached('course'.$instanceid)) {
$this->cache->{'course'.$instanceid} = $DB->get_record('course', array('id'=>$instanceid)); if ($PAGE->course->id == $instanceid) {
$this->cache->{'course'.$instanceid} = $PAGE->course;
} else {
$this->cache->{'course'.$instanceid} = $DB->get_record('course', array('id'=>$instanceid), '*', MUST_EXIST);
}
} }
$course = $this->cache->{'course'.$instanceid}; $course = $this->cache->{'course'.$instanceid};
if (!$course) {
echo "Invalid Course ID";
break;
}
if (!$this->format_display_course_content($course->format)) { if (!$this->format_display_course_content($course->format)) {
return true; return true;
} }
@@ -2052,10 +2036,9 @@ class limited_global_navigation extends global_navigation {
$keys = array(); $keys = array();
parent::load_course($keys, $course); parent::load_course($keys, $course);
if (isloggedin() && has_capability('moodle/course:view', get_context_instance(CONTEXT_COURSE, $instanceid))) { if (isloggedin() && has_capability('moodle/course:view', $this->context)) {
if (!$this->cache->cached('course'.$course->id.'section0')) { if (!$this->cache->cached('course'.$course->id.'section0')) {
$this->cache->{'course'.$course->id.'section0'} = $DB->get_record('course_sections', array('course'=>$course->id, 'section'=>'0')); $this->cache->{'course'.$course->id.'section0'} = get_course_section('0', $course->id);
} }
$section = $this->cache->{'course'.$course->id.'section0'}; $section = $this->cache->{'course'.$course->id.'section0'};
$this->load_section_activities($course, $section); $this->load_section_activities($course, $section);
@@ -2071,19 +2054,17 @@ class limited_global_navigation extends global_navigation {
*/ */
protected function load_section($instanceid=0) { protected function load_section($instanceid=0) {
global $DB, $PAGE, $CFG; global $DB, $PAGE, $CFG;
$section = $DB->get_record('course_sections', array('id'=>$instanceid));
$section = $DB->get_record('course_sections', array('id'=>$instanceid), '*', MUST_EXIST);
if (!$section) {
echo "Invalid Course Section ID";
}
if (!$this->cache->cached('course'.$section->course)) { if (!$this->cache->cached('course'.$section->course)) {
$this->cache->{'course'.$section->course} = $DB->get_record('course', array('id'=>$section->course)); if ($PAGE->course->id == $section->course) {
$this->cache->{'course'.$section->course} = $PAGE->course;
} else {
$this->cache->{'course'.$section->course} = $DB->get_record('course', array('id'=>$section->course), '*', MUST_EXIST);
}
} }
$course = $this->cache->{'course'.$section->course}; $course = $this->cache->{'course'.$section->course};
if (!$course) {
echo "Invalid Course ID";
}
if (!$this->cache->cached('coursecontext'.$course->id)) { if (!$this->cache->cached('coursecontext'.$course->id)) {
$this->cache->{'coursecontext'.$course->id} = get_context_instance(CONTEXT_COURSE, $course->id); $this->cache->{'coursecontext'.$course->id} = get_context_instance(CONTEXT_COURSE, $course->id);
@@ -2126,24 +2107,20 @@ class limited_global_navigation extends global_navigation {
* @param string $activeparam * @param string $activeparam
* @return navigation_node|bool * @return navigation_node|bool
*/ */
public function limited_load_section_generic($keys, $course, $section, $name=null, $activeparam = null) { public function limited_load_section_generic($keys, $course, $section, $name=null, $activeparam = 'topic') {
global $PAGE, $CFG; global $PAGE, $CFG;
if ($name === null) { if ($name === null) {
$name = get_string('topic'); $name = get_string('topic');
} }
if ($activeparam === null) {
$activeparam = 'topic';
}
if (!$this->cache->cached('canviewhiddensections')) { if (!$this->cache->cached('canviewhiddensections')) {
$this->cache->canviewhiddensections = has_capability('moodle/course:viewhiddensections', $this->context); $this->cache->canviewhiddensections = has_capability('moodle/course:viewhiddensections', $this->context);
} }
$viewhiddensections = $this->cache->canviewhiddensections; $viewhiddensections = $this->cache->canviewhiddensections;
$selectedstructure = optional_param($activeparam,false,PARAM_INT);
if (!$viewhiddensections && !$section->visible) { if (!$viewhiddensections && !$section->visible) {
continue; return false;
} }
if ($section->section!=0) { if ($section->section!=0) {
$url = new moodle_url('/course/view.php', array('id'=>$course->id, $activeparam=>$section->id)); $url = new moodle_url('/course/view.php', array('id'=>$course->id, $activeparam=>$section->id));
@@ -2169,7 +2146,7 @@ class limited_global_navigation extends global_navigation {
* @return void * @return void
*/ */
protected function load_section_activities($course, $section) { protected function load_section_activities($course, $section) {
global $OUTPUT, $CFG; global $CFG;
if (!is_object($section)) { if (!is_object($section)) {
return; return;
} }
@@ -2197,16 +2174,14 @@ class limited_global_navigation extends global_navigation {
$icon = new pix_icon('icon', '', $module->modname); $icon = new pix_icon('icon', '', $module->modname);
} }
$url = new moodle_url('/mod/'.$module->modname.'/view.php', array('id'=>$module->id)); $url = new moodle_url('/mod/'.$module->modname.'/view.php', array('id'=>$module->id));
$type = navigation_node::TYPE_ACTIVITY; // TODO: add some new fast test for resource types $this->add_to_path($keys, $module->id, $module->name, $module->name, navigation_node::TYPE_ACTIVITY, $url, $icon);
$child = $this->find_child($module->id, navigation_node::TYPE_ACTIVITY);
$this->add_to_path($keys, $module->id, $module->name, $module->name, $type, $url, $icon);
$child = $this->find_child($module->id, $type);
if ($child != false) { if ($child != false) {
$child->title(get_string('modulename', $module->modname)); $child->title(get_string('modulename', $module->modname));
if (!$module->visible) { if (!$module->visible) {
$child->hidden = true; $child->hidden = true;
} }
if ($type==navigation_node::TYPE_ACTIVITY && $this->module_extends_navigation($module->modname)) { if ($this->module_extends_navigation($module->modname)) {
$child->nodetype = self::NODETYPE_BRANCH; $child->nodetype = self::NODETYPE_BRANCH;
} }
} }
@@ -2221,34 +2196,18 @@ class limited_global_navigation extends global_navigation {
* @return void * @return void
*/ */
protected function load_activity($instanceid) { protected function load_activity($instanceid) {
global $DB, $CFG; global $CFG, $PAGE;
$cm = $DB->get_record('course_modules', array('id'=>$instanceid));
if (!$cm) {
echo "Invalid Course Module ID";
return;
}
$module = $DB->get_record('modules', array('id'=>$cm->module));
if (!$module) {
echo "Invalid Module ID";
return;
}
$course = $DB->get_record('course', array('id'=>$cm->course));
if (!$course) {
echo "Invalid Course ID";
return;
}
$this->context = get_context_instance(CONTEXT_COURSE, $course->id);
$key = $this->add($module->name, null, self::TYPE_ACTIVITY, null, $instanceid); $this->context = get_context_instance(CONTEXT_COURSE, $PAGE->course->id);
$key = $this->add($PAGE->activityname, null, self::TYPE_ACTIVITY, null, $instanceid);
$file = $CFG->dirroot.'/mod/'.$module->name.'/lib.php'; $file = $CFG->dirroot.'/mod/'.$PAGE->activityname.'/lib.php';
$function = $module->name.'_extend_navigation'; $function = $PAGE->activityname.'_extend_navigation';
if (file_exists($file)) { if (file_exists($file)) {
require_once($file); require_once($file);
if (function_exists($function)) { if (function_exists($function)) {
$node = $this->get($key); $function($this->get($key), $PAGE->course, $PAGE->activityrecord, $PAGE->cm);
$function($node, $course, $module, $cm);
} }
} }
} }
@@ -2285,7 +2244,7 @@ class navbar extends navigation_node {
* The almighty constructor * The almighty constructor
*/ */
public function __construct(&$page) { public function __construct(&$page) {
global $SITE, $CFG; global $CFG;
if (during_initial_install()) { if (during_initial_install()) {
$this->duringinstall = true; $this->duringinstall = true;
return false; return false;
@@ -2355,36 +2314,19 @@ class navbar extends navigation_node {
} }
// For screen readers // For screen readers
$output = get_accesshide(get_string('youarehere','access'), 'h2')."<ul>\n"; $output = get_accesshide(get_string('youarehere','access'), 'h2').html_writer::start_tag('ul');
$customchildren = (count($this->children) > 0);
// Check if navigation contains the active node // Check if navigation contains the active node
if (!$this->ignoreactive) { if (!$this->ignoreactive && $this->page->navigation->contains_active_node()) {
if ($this->page->navigation->contains_active_node()) { // Parse the navigation tree to get the active node
// Parse the navigation tree to get the active node $output .= $this->parse_branch_to_html($this->page->navigation->children, true);
$output .= $this->parse_branch_to_html($this->page->navigation->children, true, $customchildren); } else if (!$this->ignoreactive && $this->page->settingsnav->contains_active_node()) {
} else if ($this->page->settingsnav->contains_active_node()) { // Parse the settings navigation to get the active node
// Parse the settings navigation to get the active node $output .= $this->parse_branch_to_html($this->page->settingsnav->children, true);
$output .= $this->parse_branch_to_html($this->page->settingsnav->children, true, $customchildren);
} else if ($this->page->navigation->reiterate_active_nodes(URL_MATCH_BASE)) {
// Parse the navigation tree to get the active node
$output .= $this->parse_branch_to_html($this->page->navigation->children, true, $customchildren);
} else if ($this->page->settingsnav->reiterate_active_nodes(URL_MATCH_BASE)) {
// Parse the settings navigation to get the active node
$output .= $this->parse_branch_to_html($this->page->settingsnav->children, true, $customchildren);
} else {
$output .= $this->parse_branch_to_html($this, true, $customchildren);
}
} else { } else {
$output .= $this->parse_branch_to_html($this, true, $customchildren); $output .= $this->parse_branch_to_html($this, true);
} }
// Check if there are any children added by code $output .= html_writer::end_tag('ul');
if ($customchildren) {
// Add the custom children
$output .= $this->parse_branch_to_html($this->children, false, false);
}
$output .= "</ul>\n";
$this->content = $output; $this->content = $output;
return $output; return $output;
} }
@@ -2395,17 +2337,19 @@ class navbar extends navigation_node {
* @param bool $firstnode * @param bool $firstnode
* @return string HTML * @return string HTML
*/ */
protected function parse_branch_to_html($navarray, $firstnode=true, $moreafterthis=false) { protected function parse_branch_to_html($navarray, $firstnode=true) {
global $CFG; global $CFG;
$separator = get_separator(); $separator = get_separator();
$output = ''; $output = '';
if ($firstnode===true) { if ($firstnode===true) {
// If this is the first node add the class first and display the // If this is the first node add the class first and display the
// navbar properties (normally sitename) // navbar properties (normally sitename)
$output .= '<li class="first">'.parent::content(true).'</li>'; $output .= html_writer::tag('li', array('class'=>'first'), parent::content(true));
} }
$count = 0; $count = 0;
if (!is_array($navarray)) return $output; if (!is_array($navarray)) {
return $output;
}
// Iterate the navarray and display each node // Iterate the navarray and display each node
while (count($navarray)>0) { while (count($navarray)>0) {
// Sanity check make sure we don't display WAY too much information // Sanity check make sure we don't display WAY too much information
@@ -2434,19 +2378,25 @@ class navbar extends navigation_node {
$navarray = $child->children; $navarray = $child->children;
// If there are not more arrays being processed after this AND this is the last element // If there are not more arrays being processed after this AND this is the last element
// then we want to set the action to null so that it is not used // then we want to set the action to null so that it is not used
if (!$moreafterthis && (!$child->contains_active_node() || ($child->find_active_node()==false || $child->find_active_node()->mainnavonly))) { if (empty($this->children) && (!$child->contains_active_node() || ($child->find_active_node()==false || $child->find_active_node()->mainnavonly))) {
$oldaction = $child->action; $oldaction = $child->action;
$child->action = null; $child->action = null;
} }
if ($child->type !== navigation_node::TYPE_CATEGORY || !isset($CFG->navshowcategories) || !empty($CFG->navshowcategories)) { if ($child->type !== navigation_node::TYPE_CATEGORY || !isset($CFG->navshowcategories) || !empty($CFG->navshowcategories)) {
// Now display the node // Now display the node
$output .= '<li>'.$separator.' '.$child->content(true).'</li>'; $output .= html_writer::tag('li', null, $separator.' '.$child->content(true));
} }
if (isset($oldaction)) { if (isset($oldaction)) {
$child->action = $oldaction; $child->action = $oldaction;
} }
} }
} }
if (!empty($this->children)) {
// Add the custom children
$output .= $this->parse_branch_to_html($this->children, false, false);
}
// XHTML // XHTML
return $output; return $output;
} }
@@ -2504,11 +2454,10 @@ class settings_navigation extends navigation_node {
/** /**
* Sets up the object with basic settings and preparse it for use * Sets up the object with basic settings and preparse it for use
*/ */
public function __construct(&$page) { public function __construct(moodle_page &$page) {
if (during_initial_install()) { if (during_initial_install()) {
return false; return false;
} }
static $settingsnavcount;
$this->page = $page; $this->page = $page;
// Initialise the main navigation. It is most important that this is done // Initialise the main navigation. It is most important that this is done
// before we try anything // before we try anything
@@ -2523,7 +2472,6 @@ class settings_navigation extends navigation_node {
* by calling supporting functions to generate major parts of the tree. * by calling supporting functions to generate major parts of the tree.
*/ */
public function initialise() { public function initialise() {
global $SITE, $OUTPUT, $CFG;
if (during_initial_install()) { if (during_initial_install()) {
return false; return false;
} }
@@ -2554,7 +2502,7 @@ class settings_navigation extends navigation_node {
$adminkey = $this->load_administration_settings(); $adminkey = $this->load_administration_settings();
} else { } else {
$this->load_front_page_settings(); $this->load_front_page_settings();
$settingskey = $this->load_user_settings($SITE->id); $settingskey = $this->load_user_settings(SITEID);
$adminkey = $this->load_administration_settings(); $adminkey = $this->load_administration_settings();
} }
break; break;
@@ -2658,7 +2606,7 @@ class settings_navigation extends navigation_node {
* @return mixed A key to access the admin tree by * @return mixed A key to access the admin tree by
*/ */
protected function load_administration_settings(navigation_node $referencebranch=null, part_of_admin_tree $adminbranch=null) { protected function load_administration_settings(navigation_node $referencebranch=null, part_of_admin_tree $adminbranch=null) {
global $CFG, $OUTPUT, $FULLME, $PAGE; global $CFG;
// Check if we are just starting to generate this navigation. // Check if we are just starting to generate this navigation.
if ($referencebranch === null) { if ($referencebranch === null) {
@@ -2674,7 +2622,7 @@ class settings_navigation extends navigation_node {
} }
if (!$this->contains_active_node()) { if (!$this->contains_active_node()) {
$section = $PAGE->url->param('section'); $section = $this->page->url->param('section');
if ($current = $adminroot->locate($section, true)) { if ($current = $adminroot->locate($section, true)) {
if ($child = $this->find_child($current->name, self::TYPE_SETTING)) { if ($child = $this->find_child($current->name, self::TYPE_SETTING)) {
$child->make_active(); $child->make_active();
@@ -2786,7 +2734,7 @@ class settings_navigation extends navigation_node {
* @return bool|mixed Either false of a key to access the course tree by * @return bool|mixed Either false of a key to access the course tree by
*/ */
protected function load_course_settings() { protected function load_course_settings() {
global $CFG, $OUTPUT, $USER, $SESSION; global $CFG, $USER, $SESSION;
$course = $this->page->course; $course = $this->page->course;
if (empty($course->context)) { if (empty($course->context)) {
@@ -3092,22 +3040,12 @@ class settings_navigation extends navigation_node {
* @return void|mixed The key to access the module method by * @return void|mixed The key to access the module method by
*/ */
protected function load_module_settings() { protected function load_module_settings() {
global $CFG, $DB; global $CFG;
if (!$this->page->cm && $this->context->contextlevel == CONTEXT_MODULE && $this->context->instanceid) { if (!$this->page->cm && $this->context->contextlevel == CONTEXT_MODULE && $this->context->instanceid) {
$cm = get_coursemodule_from_id('chat', $this->context->instanceid); $cm = get_coursemodule_from_id('chat', $this->context->instanceid, 0, false, MUST_EXIST);
$cm->context = $this->context; $cm->context = $this->context;
if ($cm) { $this->page->set_cm($cm, $this->page->course);
$this->page->set_cm($cm, $this->page->course);
} else {
debugging('The module has not been set against the page but we are attempting to generate module specific information for navigation', DEBUG_DEVELOPER);
return;
}
}
if (!$this->page->cm) {
debugging('The module has not been set against the page but we are attempting to generate module specific information for navigation', DEBUG_DEVELOPER);
return;
} }
if (empty($this->page->cm->context)) { if (empty($this->page->cm->context)) {
@@ -3118,14 +3056,8 @@ class settings_navigation extends navigation_node {
} }
} }
$module = $DB->get_record('modules', array('id'=>$this->page->cm->module)); $file = $CFG->dirroot.'/mod/'.$this->page->activityname.'/lib.php';
if (!$module) { $function = $this->page->activityname.'_extend_settings_navigation';
debugging('Invalid Module ID picked up while attempting to load the activity for the navigation', DEBUG_DEVELOPER);
return;
}
$file = $CFG->dirroot.'/mod/'.$module->name.'/lib.php';
$function = $module->name.'_extend_settings_navigation';
if (file_exists($file)) { if (file_exists($file)) {
require_once($file); require_once($file);
@@ -3133,7 +3065,7 @@ class settings_navigation extends navigation_node {
if (!function_exists($function)) { if (!function_exists($function)) {
return; return;
} }
return $function($this,$module); return $function($this,$this->page->activityrecord);
} }
/** /**
@@ -3188,14 +3120,20 @@ class settings_navigation extends navigation_node {
* @return string|int The key to reference this user's settings * @return string|int The key to reference this user's settings
*/ */
protected function generate_user_settings($courseid, $userid, $gstitle='usercurrentsettings') { protected function generate_user_settings($courseid, $userid, $gstitle='usercurrentsettings') {
global $DB, $CFG, $USER; global $DB, $CFG, $USER, $SITE;
if (!$course = $DB->get_record("course", array("id"=>$courseid))) { if ($courseid != SITEID) {
return false; if (!empty($this->page->course->id) && $this->page->course->id == $courseid) {
$course = $this->page->course;
} else {
$course = $DB->get_record("course", array("id"=>$courseid), '*', MUST_EXIST);
}
} else {
$course = $SITE;
} }
$coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); // Course context $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); // Course context
$systemcontext = get_context_instance(CONTEXT_SYSTEM); $systemcontext = get_system_context();
$currentuser = ($USER->id == $userid); $currentuser = ($USER->id == $userid);
if ($currentuser) { if ($currentuser) {
@@ -3369,17 +3307,19 @@ class settings_navigation extends navigation_node {
* This function is called when the user is on the front page, or $COURSE==$SITE * This function is called when the user is on the front page, or $COURSE==$SITE
*/ */
protected function load_front_page_settings() { protected function load_front_page_settings() {
global $CFG, $USER, $OUTPUT, $SITE; global $SITE;
$course = $SITE; $course = $SITE;
if (empty($course->context)) { if (empty($course->context)) {
$course->context = get_context_instance(CONTEXT_COURSE, $course->id); // Course context $course->context = get_context_instance(CONTEXT_COURSE, $course->id); // Course context
} }
if (has_capability('moodle/course:update', $course->context)) {
$frontpage = $this->add(get_string('frontpagesettings'), null, self::TYPE_SETTING, null, 'frontpage'); $frontpagekey = $this->add(get_string('frontpagesettings'), null, self::TYPE_SETTING, null, 'frontpage');
$this->get($frontpage)->id = 'frontpagesettings'; $frontpage = $this->get($frontpagekey);
$this->get($frontpage)->forceopen = true;
if (has_capability('moodle/course:update', $course->context)) {
$frontpage->id = 'frontpagesettings';
$frontpage->forceopen = true;
// Add the turn on/off settings // Add the turn on/off settings
$url = new moodle_url('/course/view.php', array('id'=>$course->id, 'sesskey'=>sesskey())); $url = new moodle_url('/course/view.php', array('id'=>$course->id, 'sesskey'=>sesskey()));
@@ -3390,17 +3330,17 @@ class settings_navigation extends navigation_node {
$url->param('edit', 'on'); $url->param('edit', 'on');
$editstring = get_string('turneditingon'); $editstring = get_string('turneditingon');
} }
$this->get($frontpage)->add($editstring, $url, self::TYPE_SETTING, null, null, new pix_icon('i/edit', '')); $frontpage->add($editstring, $url, self::TYPE_SETTING, null, null, new pix_icon('i/edit', ''));
// Add the course settings link // Add the course settings link
$url = new moodle_url('/admin/settings.php', array('section'=>'frontpagesettings')); $url = new moodle_url('/admin/settings.php', array('section'=>'frontpagesettings'));
$this->get($frontpage)->add(get_string('settings'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/settings', '')); $frontpage->add(get_string('settings'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/settings', ''));
} }
//Participants //Participants
if (has_capability('moodle/site:viewparticipants', $course->context)) { if (has_capability('moodle/site:viewparticipants', $course->context)) {
$url = new moodle_url('/user/index.php?contextid='.$course->context->id); $url = new moodle_url('/user/index.php', array('contextid'=>$course->context->id));
$this->get($frontpage)->add(get_string('participants'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/users', '')); $frontpage->add(get_string('participants'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/users', ''));
} }
} }
@@ -3432,7 +3372,7 @@ class settings_navigation extends navigation_node {
* @copyright 2009 Sam Hemelryk * @copyright 2009 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/ */
class navigation_xml { class navigation_json {
/** @var array */ /** @var array */
protected $nodetype = array('node','branch'); protected $nodetype = array('node','branch');
/** @var array */ /** @var array */
@@ -3481,6 +3421,7 @@ class navigation_xml {
} }
$attributes = array(); $attributes = array();
$attributes['id'] = $child->id; $attributes['id'] = $child->id;
$attributes['name'] = $child->text;
$attributes['type'] = $child->type; $attributes['type'] = $child->type;
$attributes['key'] = $child->key; $attributes['key'] = $child->key;
$attributes['icon'] = $child->icon; $attributes['icon'] = $child->icon;
@@ -3506,33 +3447,22 @@ class navigation_xml {
$attributes['hidden'] = ($child->hidden); $attributes['hidden'] = ($child->hidden);
$attributes['haschildren'] = (count($child->children)>0 || $child->type == navigation_node::TYPE_CATEGORY); $attributes['haschildren'] = (count($child->children)>0 || $child->type == navigation_node::TYPE_CATEGORY);
$xml = '<'.$this->nodetype[$child->nodetype]; if ($attributes['icon'] instanceof pix_icon) {
if (count($attributes)>0) { $attributes['icon'] = $OUTPUT->pix_url($attributes['icon']->pix, $attributes['icon']->component);
foreach ($attributes as $key=>$value) {
if (is_bool($value)) {
if ($value) {
$xml .= ' '.$key.'="true"';
} else {
$xml .= ' '.$key.'="false"';
}
} else if ($value instanceof pix_icon) {
$xml .= ' '.$key.'="'.$OUTPUT->pix_url($value->pix, $value->component).'"';
} else if ($value !== null) {
$xml .= ' '.$key.'="'.$value.'"';
}
}
} }
$xml .= '>';
$xml .= '<name>'.htmlentities($child->text).'</name>';
if (count($child->children)>0) { if (count($child->children)>0) {
$xml .= '<children>'; $attributes['children'] = array();
foreach ($child->children as $subchild) { foreach ($child->children as $subchild) {
$xml .= $this->convert_child($subchild, $depth+1); $attributes['children'][] = $this->convert_child($subchild, $depth+1);
} }
$xml .= '</children>';
} }
$xml .= '</'.$this->nodetype[$child->nodetype].'>';
return $xml; if ($depth > 1) {
return $attributes;
} else {
return json_encode($attributes);
}
} }
} }
@@ -3587,7 +3517,7 @@ class navigation_cache {
* @param int $timeout The number of seconds to time the information out after * @param int $timeout The number of seconds to time the information out after
*/ */
public function __construct($area, $timeout=60) { public function __construct($area, $timeout=60) {
global $SESSION, $PAGE; global $SESSION;
$this->creation = time(); $this->creation = time();
$this->area = $area; $this->area = $area;
@@ -3729,4 +3659,4 @@ class navigation_cache {
} }
} }
} }
} }