|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 00003 // This file is part of Moodle - http://moodle.org/ 00004 // 00005 // Moodle is free software: you can redistribute it and/or modify 00006 // it under the terms of the GNU General Public License as published by 00007 // the Free Software Foundation, either version 3 of the License, or 00008 // (at your option) any later version. 00009 // 00010 // Moodle is distributed in the hope that it will be useful, 00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 // GNU General Public License for more details. 00014 // 00015 // You should have received a copy of the GNU General Public License 00016 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 00017 00029 defined('MOODLE_INTERNAL') || die(); 00030 00034 define('NAVIGATION_CACHE_NAME', 'navigation'); 00035 00050 class navigation_node implements renderable { 00052 const NODETYPE_LEAF = 0; 00054 const NODETYPE_BRANCH = 1; 00056 const TYPE_UNKNOWN = null; 00058 const TYPE_ROOTNODE = 0; 00060 const TYPE_SYSTEM = 1; 00062 const TYPE_CATEGORY = 10; 00064 const TYPE_COURSE = 20; 00066 const TYPE_SECTION = 30; 00068 const TYPE_ACTIVITY = 40; 00070 const TYPE_RESOURCE = 50; 00072 const TYPE_CUSTOM = 60; 00074 const TYPE_SETTING = 70; 00076 const TYPE_USER = 80; 00078 const TYPE_CONTAINER = 90; 00079 00081 public $id = null; 00083 public $key = null; 00085 public $text = null; 00087 public $shorttext = null; 00089 public $title = null; 00091 public $helpbutton = null; 00093 public $action = null; 00095 public $icon = null; 00097 public $type = self::TYPE_UNKNOWN; 00099 public $nodetype = self::NODETYPE_LEAF; 00101 public $collapse = false; 00103 public $forceopen = false; 00105 public $classes = array(); 00107 public $children = array(); 00109 public $isactive = false; 00111 public $hidden = false; 00113 public $display = true; 00115 public $preceedwithhr = false; 00117 public $mainnavonly = false; 00119 public $forcetitle = false; 00121 public $parent = null; 00123 public $hideicon = false; 00125 protected $namedtypes = array(0=>'system',10=>'category',20=>'course',30=>'structure',40=>'activity',50=>'resource',60=>'custom',70=>'setting', 80=>'user'); 00127 protected static $fullmeurl = null; 00129 public static $autofindactive = true; 00131 public $includesectionnum = false; 00132 00139 public function __construct($properties) { 00140 if (is_array($properties)) { 00141 // Check the array for each property that we allow to set at construction. 00142 // text - The main content for the node 00143 // shorttext - A short text if required for the node 00144 // icon - The icon to display for the node 00145 // type - The type of the node 00146 // key - The key to use to identify the node 00147 // parent - A reference to the nodes parent 00148 // action - The action to attribute to this node, usually a URL to link to 00149 if (array_key_exists('text', $properties)) { 00150 $this->text = $properties['text']; 00151 } 00152 if (array_key_exists('shorttext', $properties)) { 00153 $this->shorttext = $properties['shorttext']; 00154 } 00155 if (!array_key_exists('icon', $properties)) { 00156 $properties['icon'] = new pix_icon('i/navigationitem', 'moodle'); 00157 } 00158 $this->icon = $properties['icon']; 00159 if ($this->icon instanceof pix_icon) { 00160 if (empty($this->icon->attributes['class'])) { 00161 $this->icon->attributes['class'] = 'navicon'; 00162 } else { 00163 $this->icon->attributes['class'] .= ' navicon'; 00164 } 00165 } 00166 if (array_key_exists('type', $properties)) { 00167 $this->type = $properties['type']; 00168 } else { 00169 $this->type = self::TYPE_CUSTOM; 00170 } 00171 if (array_key_exists('key', $properties)) { 00172 $this->key = $properties['key']; 00173 } 00174 // This needs to happen last because of the check_if_active call that occurs 00175 if (array_key_exists('action', $properties)) { 00176 $this->action = $properties['action']; 00177 if (is_string($this->action)) { 00178 $this->action = new moodle_url($this->action); 00179 } 00180 if (self::$autofindactive) { 00181 $this->check_if_active(); 00182 } 00183 } 00184 if (array_key_exists('parent', $properties)) { 00185 $this->set_parent($properties['parent']); 00186 } 00187 } else if (is_string($properties)) { 00188 $this->text = $properties; 00189 } 00190 if ($this->text === null) { 00191 throw new coding_exception('You must set the text for the node when you create it.'); 00192 } 00193 // Default the title to the text 00194 $this->title = $this->text; 00195 // Instantiate a new navigation node collection for this nodes children 00196 $this->children = new navigation_node_collection(); 00197 } 00198 00208 public function check_if_active($strength=URL_MATCH_EXACT) { 00209 global $FULLME, $PAGE; 00210 // Set fullmeurl if it hasn't already been set 00211 if (self::$fullmeurl == null) { 00212 if ($PAGE->has_set_url()) { 00213 self::override_active_url(new moodle_url($PAGE->url)); 00214 } else { 00215 self::override_active_url(new moodle_url($FULLME)); 00216 } 00217 } 00218 00219 // Compare the action of this node against the fullmeurl 00220 if ($this->action instanceof moodle_url && $this->action->compare(self::$fullmeurl, $strength)) { 00221 $this->make_active(); 00222 return true; 00223 } 00224 return false; 00225 } 00226 00236 public static function override_active_url(moodle_url $url) { 00237 // Clone the URL, in case the calling script changes their URL later. 00238 self::$fullmeurl = new moodle_url($url); 00239 } 00240 00252 public static function create($text, $action=null, $type=self::TYPE_CUSTOM, 00253 $shorttext=null, $key=null, pix_icon $icon=null) { 00254 // Properties array used when creating the new navigation node 00255 $itemarray = array( 00256 'text' => $text, 00257 'type' => $type 00258 ); 00259 // Set the action if one was provided 00260 if ($action!==null) { 00261 $itemarray['action'] = $action; 00262 } 00263 // Set the shorttext if one was provided 00264 if ($shorttext!==null) { 00265 $itemarray['shorttext'] = $shorttext; 00266 } 00267 // Set the icon if one was provided 00268 if ($icon!==null) { 00269 $itemarray['icon'] = $icon; 00270 } 00271 // Set the key 00272 $itemarray['key'] = $key; 00273 // Construct and return 00274 return new navigation_node($itemarray); 00275 } 00276 00288 public function add($text, $action=null, $type=self::TYPE_CUSTOM, $shorttext=null, $key=null, pix_icon $icon=null) { 00289 // Create child node 00290 $childnode = self::create($text, $action, $type, $shorttext, $key, $icon); 00291 00292 // Add the child to end and return 00293 return $this->add_node($childnode); 00294 } 00295 00304 public function add_node(navigation_node $childnode, $beforekey=null) { 00305 // First convert the nodetype for this node to a branch as it will now have children 00306 if ($this->nodetype !== self::NODETYPE_BRANCH) { 00307 $this->nodetype = self::NODETYPE_BRANCH; 00308 } 00309 // Set the parent to this node 00310 $childnode->set_parent($this); 00311 00312 // Default the key to the number of children if not provided 00313 if ($childnode->key === null) { 00314 $childnode->key = $this->children->count(); 00315 } 00316 00317 // Add the child using the navigation_node_collections add method 00318 $node = $this->children->add($childnode, $beforekey); 00319 00320 // If added node is a category node or the user is logged in and it's a course 00321 // then mark added node as a branch (makes it expandable by AJAX) 00322 $type = $childnode->type; 00323 if (($type==self::TYPE_CATEGORY) || (isloggedin() && $type==self::TYPE_COURSE)) { 00324 $node->nodetype = self::NODETYPE_BRANCH; 00325 } 00326 // If this node is hidden mark it's children as hidden also 00327 if ($this->hidden) { 00328 $node->hidden = true; 00329 } 00330 // Return added node (reference returned by $this->children->add() 00331 return $node; 00332 } 00333 00338 public function get_children_key_list() { 00339 return $this->children->get_key_list(); 00340 } 00341 00353 public function find($key, $type) { 00354 return $this->children->find($key, $type); 00355 } 00356 00367 public function get($key, $type=null) { 00368 return $this->children->get($key, $type); 00369 } 00370 00376 public function remove() { 00377 return $this->parent->children->remove($this->key, $this->type); 00378 } 00379 00385 public function has_children() { 00386 return ($this->nodetype === navigation_node::NODETYPE_BRANCH || $this->children->count()>0); 00387 } 00388 00397 public function make_active() { 00398 $this->isactive = true; 00399 $this->add_class('active_tree_node'); 00400 $this->force_open(); 00401 if ($this->parent !== null) { 00402 $this->parent->make_inactive(); 00403 } 00404 } 00405 00410 public function make_inactive() { 00411 $this->isactive = false; 00412 $this->remove_class('active_tree_node'); 00413 if ($this->parent !== null) { 00414 $this->parent->make_inactive(); 00415 } 00416 } 00417 00424 public function force_open() { 00425 $this->forceopen = true; 00426 if ($this->parent !== null) { 00427 $this->parent->force_open(); 00428 } 00429 } 00430 00437 public function add_class($class) { 00438 if (!in_array($class, $this->classes)) { 00439 $this->classes[] = $class; 00440 } 00441 return true; 00442 } 00443 00450 public function remove_class($class) { 00451 if (in_array($class, $this->classes)) { 00452 $key = array_search($class,$this->classes); 00453 if ($key!==false) { 00454 unset($this->classes[$key]); 00455 return true; 00456 } 00457 } 00458 return false; 00459 } 00460 00465 public function title($title) { 00466 $this->title = $title; 00467 $this->forcetitle = true; 00468 } 00469 00473 public function __wakeup(){ 00474 $this->forceopen = false; 00475 $this->isactive = false; 00476 $this->remove_class('active_tree_node'); 00477 } 00478 00486 public function contains_active_node() { 00487 if ($this->isactive) { 00488 return true; 00489 } else { 00490 foreach ($this->children as $child) { 00491 if ($child->isactive || $child->contains_active_node()) { 00492 return true; 00493 } 00494 } 00495 } 00496 return false; 00497 } 00498 00509 public function find_active_node() { 00510 if ($this->isactive) { 00511 return $this; 00512 } else { 00513 foreach ($this->children as &$child) { 00514 $outcome = $child->find_active_node(); 00515 if ($outcome !== false) { 00516 return $outcome; 00517 } 00518 } 00519 } 00520 return false; 00521 } 00522 00527 public function search_for_active_node() { 00528 if ($this->check_if_active(URL_MATCH_BASE)) { 00529 return $this; 00530 } else { 00531 foreach ($this->children as &$child) { 00532 $outcome = $child->search_for_active_node(); 00533 if ($outcome !== false) { 00534 return $outcome; 00535 } 00536 } 00537 } 00538 return false; 00539 } 00540 00547 public function get_content($shorttext=false) { 00548 if ($shorttext && $this->shorttext!==null) { 00549 return format_string($this->shorttext); 00550 } else { 00551 return format_string($this->text); 00552 } 00553 } 00554 00560 public function get_title() { 00561 if ($this->forcetitle || $this->action != null){ 00562 return $this->title; 00563 } else { 00564 return ''; 00565 } 00566 } 00567 00573 public function get_css_type() { 00574 if (array_key_exists($this->type, $this->namedtypes)) { 00575 return 'type_'.$this->namedtypes[$this->type]; 00576 } 00577 return 'type_unknown'; 00578 } 00579 00585 public function find_expandable(array &$expandable) { 00586 foreach ($this->children as &$child) { 00587 if ($child->nodetype == self::NODETYPE_BRANCH && $child->children->count() == 0 && $child->display) { 00588 $child->id = 'expandable_branch_'.(count($expandable)+1); 00589 $this->add_class('canexpand'); 00590 $expandable[] = array('id' => $child->id, 'key' => $child->key, 'type' => $child->type); 00591 } 00592 $child->find_expandable($expandable); 00593 } 00594 } 00595 00602 public function find_all_of_type($type) { 00603 $nodes = $this->children->type($type); 00604 foreach ($this->children as &$node) { 00605 $childnodes = $node->find_all_of_type($type); 00606 $nodes = array_merge($nodes, $childnodes); 00607 } 00608 return $nodes; 00609 } 00610 00614 public function trim_if_empty() { 00615 if ($this->children->count() == 0) { 00616 $this->remove(); 00617 } 00618 } 00619 00630 public function get_tabs_array(array $inactive=array(), $return=false) { 00631 $tabs = array(); 00632 $rows = array(); 00633 $selected = null; 00634 $activated = array(); 00635 foreach ($this->children as $node) { 00636 $tabs[] = new tabobject($node->key, $node->action, $node->get_content(), $node->get_title()); 00637 if ($node->contains_active_node()) { 00638 if ($node->children->count() > 0) { 00639 $activated[] = $node->key; 00640 foreach ($node->children as $child) { 00641 if ($child->contains_active_node()) { 00642 $selected = $child->key; 00643 } 00644 $rows[] = new tabobject($child->key, $child->action, $child->get_content(), $child->get_title()); 00645 } 00646 } else { 00647 $selected = $node->key; 00648 } 00649 } 00650 } 00651 return array(array($tabs, $rows), $selected, $inactive, $activated, $return); 00652 } 00653 00660 public function set_parent(navigation_node $parent) { 00661 // Set the parent (thats the easy part) 00662 $this->parent = $parent; 00663 // Check if this node is active (this is checked during construction) 00664 if ($this->isactive) { 00665 // Force all of the parent nodes open so you can see this node 00666 $this->parent->force_open(); 00667 // Make all parents inactive so that its clear where we are. 00668 $this->parent->make_inactive(); 00669 } 00670 } 00671 } 00672 00689 class navigation_node_collection implements IteratorAggregate { 00695 protected $collection = array(); 00701 protected $orderedcollection = array(); 00706 protected $last = null; 00711 protected $count = 0; 00712 00721 public function add(navigation_node $node, $beforekey=null) { 00722 global $CFG; 00723 $key = $node->key; 00724 $type = $node->type; 00725 00726 // First check we have a 2nd dimension for this type 00727 if (!array_key_exists($type, $this->orderedcollection)) { 00728 $this->orderedcollection[$type] = array(); 00729 } 00730 // Check for a collision and report if debugging is turned on 00731 if ($CFG->debug && array_key_exists($key, $this->orderedcollection[$type])) { 00732 debugging('Navigation node intersect: Adding a node that already exists '.$key, DEBUG_DEVELOPER); 00733 } 00734 00735 // Find the key to add before 00736 $newindex = $this->count; 00737 $last = true; 00738 if ($beforekey !== null) { 00739 foreach ($this->collection as $index => $othernode) { 00740 if ($othernode->key === $beforekey) { 00741 $newindex = $index; 00742 $last = false; 00743 break; 00744 } 00745 } 00746 if ($newindex === $this->count) { 00747 debugging('Navigation node add_before: Reference node not found ' . $beforekey . 00748 ', options: ' . implode(' ', $this->get_key_list()), DEBUG_DEVELOPER); 00749 } 00750 } 00751 00752 // Add the node to the appropriate place in the by-type structure (which 00753 // is not ordered, despite the variable name) 00754 $this->orderedcollection[$type][$key] = $node; 00755 if (!$last) { 00756 // Update existing references in the ordered collection (which is the 00757 // one that isn't called 'ordered') to shuffle them along if required 00758 for ($oldindex = $this->count; $oldindex > $newindex; $oldindex--) { 00759 $this->collection[$oldindex] = $this->collection[$oldindex - 1]; 00760 } 00761 } 00762 // Add a reference to the node to the progressive collection. 00763 $this->collection[$newindex] = &$this->orderedcollection[$type][$key]; 00764 // Update the last property to a reference to this new node. 00765 $this->last = &$this->orderedcollection[$type][$key]; 00766 00767 // Reorder the array by index if needed 00768 if (!$last) { 00769 ksort($this->collection); 00770 } 00771 $this->count++; 00772 // Return the reference to the now added node 00773 return $node; 00774 } 00775 00780 public function get_key_list() { 00781 $keys = array(); 00782 foreach ($this->collection as $node) { 00783 $keys[] = $node->key; 00784 } 00785 return $keys; 00786 } 00787 00795 public function get($key, $type=null) { 00796 if ($type !== null) { 00797 // If the type is known then we can simply check and fetch 00798 if (!empty($this->orderedcollection[$type][$key])) { 00799 return $this->orderedcollection[$type][$key]; 00800 } 00801 } else { 00802 // Because we don't know the type we look in the progressive array 00803 foreach ($this->collection as $node) { 00804 if ($node->key === $key) { 00805 return $node; 00806 } 00807 } 00808 } 00809 return false; 00810 } 00811 00824 public function find($key, $type=null) { 00825 if ($type !== null && array_key_exists($type, $this->orderedcollection) && array_key_exists($key, $this->orderedcollection[$type])) { 00826 return $this->orderedcollection[$type][$key]; 00827 } else { 00828 $nodes = $this->getIterator(); 00829 // Search immediate children first 00830 foreach ($nodes as &$node) { 00831 if ($node->key === $key && ($type === null || $type === $node->type)) { 00832 return $node; 00833 } 00834 } 00835 // Now search each childs children 00836 foreach ($nodes as &$node) { 00837 $result = $node->children->find($key, $type); 00838 if ($result !== false) { 00839 return $result; 00840 } 00841 } 00842 } 00843 return false; 00844 } 00845 00851 public function last() { 00852 return $this->last; 00853 } 00854 00858 public function type($type) { 00859 if (!array_key_exists($type, $this->orderedcollection)) { 00860 $this->orderedcollection[$type] = array(); 00861 } 00862 return $this->orderedcollection[$type]; 00863 } 00871 public function remove($key, $type=null) { 00872 $child = $this->get($key, $type); 00873 if ($child !== false) { 00874 foreach ($this->collection as $colkey => $node) { 00875 if ($node->key == $key && $node->type == $type) { 00876 unset($this->collection[$colkey]); 00877 break; 00878 } 00879 } 00880 unset($this->orderedcollection[$child->type][$child->key]); 00881 $this->count--; 00882 return true; 00883 } 00884 return false; 00885 } 00886 00895 public function count() { 00896 return $this->count; 00897 } 00906 public function getIterator() { 00907 return new ArrayIterator($this->collection); 00908 } 00909 } 00910 00928 class global_navigation extends navigation_node { 00933 protected $page; 00935 protected $initialised = false; 00937 protected $mycourses = array(); 00939 protected $rootnodes = array(); 00941 protected $showemptysections = false; 00943 protected $showcategories = null; 00945 protected $extendforuser = array(); 00947 protected $cache; 00949 protected $addedcourses = array(); 00951 protected $addedcategories = array(); 00953 protected $expansionlimit = 0; 00955 protected $useridtouseforparentchecks = 0; 00956 00962 public function __construct(moodle_page $page) { 00963 global $CFG, $SITE, $USER; 00964 00965 if (during_initial_install()) { 00966 return; 00967 } 00968 00969 if (get_home_page() == HOMEPAGE_SITE) { 00970 // We are using the site home for the root element 00971 $properties = array( 00972 'key' => 'home', 00973 'type' => navigation_node::TYPE_SYSTEM, 00974 'text' => get_string('home'), 00975 'action' => new moodle_url('/') 00976 ); 00977 } else { 00978 // We are using the users my moodle for the root element 00979 $properties = array( 00980 'key' => 'myhome', 00981 'type' => navigation_node::TYPE_SYSTEM, 00982 'text' => get_string('myhome'), 00983 'action' => new moodle_url('/my/') 00984 ); 00985 } 00986 00987 // Use the parents consturctor.... good good reuse 00988 parent::__construct($properties); 00989 00990 // Initalise and set defaults 00991 $this->page = $page; 00992 $this->forceopen = true; 00993 $this->cache = new navigation_cache(NAVIGATION_CACHE_NAME); 00994 } 00995 01004 public function set_userid_for_parent_checks($userid) { 01005 $this->useridtouseforparentchecks = $userid; 01006 } 01007 01008 01021 public function initialise() { 01022 global $CFG, $SITE, $USER, $DB; 01023 // Check if it has alread been initialised 01024 if ($this->initialised || during_initial_install()) { 01025 return true; 01026 } 01027 $this->initialised = true; 01028 01029 // Set up the five base root nodes. These are nodes where we will put our 01030 // content and are as follows: 01031 // site: Navigation for the front page. 01032 // myprofile: User profile information goes here. 01033 // mycourses: The users courses get added here. 01034 // courses: Additional courses are added here. 01035 // users: Other users information loaded here. 01036 $this->rootnodes = array(); 01037 if (get_home_page() == HOMEPAGE_SITE) { 01038 // The home element should be my moodle because the root element is the site 01039 if (isloggedin() && !isguestuser()) { // Makes no sense if you aren't logged in 01040 $this->rootnodes['home'] = $this->add(get_string('myhome'), new moodle_url('/my/'), self::TYPE_SETTING, null, 'home'); 01041 } 01042 } else { 01043 // The home element should be the site because the root node is my moodle 01044 $this->rootnodes['home'] = $this->add(get_string('sitehome'), new moodle_url('/'), self::TYPE_SETTING, null, 'home'); 01045 if ($CFG->defaulthomepage == HOMEPAGE_MY) { 01046 // We need to stop automatic redirection 01047 $this->rootnodes['home']->action->param('redirect', '0'); 01048 } 01049 } 01050 $this->rootnodes['site'] = $this->add_course($SITE); 01051 $this->rootnodes['myprofile'] = $this->add(get_string('myprofile'), null, self::TYPE_USER, null, 'myprofile'); 01052 $this->rootnodes['mycourses'] = $this->add(get_string('mycourses'), null, self::TYPE_ROOTNODE, null, 'mycourses'); 01053 $this->rootnodes['courses'] = $this->add(get_string('courses'), null, self::TYPE_ROOTNODE, null, 'courses'); 01054 $this->rootnodes['users'] = $this->add(get_string('users'), null, self::TYPE_ROOTNODE, null, 'users'); 01055 01056 // Fetch all of the users courses. 01057 $limit = 20; 01058 if (!empty($CFG->navcourselimit)) { 01059 $limit = $CFG->navcourselimit; 01060 } 01061 01062 $mycourses = enrol_get_my_courses(NULL, 'visible DESC,sortorder ASC', $limit); 01063 $showallcourses = (count($mycourses) == 0 || !empty($CFG->navshowallcourses)); 01064 $showcategories = ($showallcourses && $this->show_categories()); 01065 $issite = ($this->page->course->id == SITEID); 01066 $ismycourse = (array_key_exists($this->page->course->id, $mycourses)); 01067 01068 // Check if any courses were returned. 01069 if (count($mycourses) > 0) { 01070 // Add all of the users courses to the navigation 01071 foreach ($mycourses as $course) { 01072 $course->coursenode = $this->add_course($course, false, true); 01073 } 01074 } 01075 01076 if ($showallcourses) { 01077 // Load all courses 01078 $this->load_all_courses(); 01079 } 01080 01081 // We always load the frontpage course to ensure it is available without 01082 // JavaScript enabled. 01083 $frontpagecourse = $this->load_course($SITE); 01084 $this->add_front_page_course_essentials($frontpagecourse, $SITE); 01085 $this->load_course_sections($SITE, $frontpagecourse); 01086 01087 $canviewcourseprofile = true; 01088 01089 if (!$issite) { 01090 // Next load context specific content into the navigation 01091 switch ($this->page->context->contextlevel) { 01092 case CONTEXT_SYSTEM : 01093 // This has already been loaded we just need to map the variable 01094 $coursenode = $frontpagecourse; 01095 $this->load_all_categories(null, $showcategories); 01096 break; 01097 case CONTEXT_COURSECAT : 01098 // This has already been loaded we just need to map the variable 01099 $coursenode = $frontpagecourse; 01100 $this->load_all_categories($this->page->context->instanceid, $showcategories); 01101 break; 01102 case CONTEXT_BLOCK : 01103 case CONTEXT_COURSE : 01104 // Load the course associated with the page into the navigation 01105 $course = $this->page->course; 01106 if ($showcategories && !$ismycourse) { 01107 $this->load_all_categories($course->category, $showcategories); 01108 } 01109 $coursenode = $this->load_course($course); 01110 01111 // If the course wasn't added then don't try going any further. 01112 if (!$coursenode) { 01113 $canviewcourseprofile = false; 01114 break; 01115 } 01116 01117 // If the user is not enrolled then we only want to show the 01118 // course node and not populate it. 01119 01120 // Not enrolled, can't view, and hasn't switched roles 01121 if (!can_access_course($course)) { 01122 // TODO: very ugly hack - do not force "parents" to enrol into course their child is enrolled in, 01123 // this hack has been propagated from user/view.php to display the navigation node. (MDL-25805) 01124 $isparent = false; 01125 if ($this->useridtouseforparentchecks) { 01126 if ($this->useridtouseforparentchecks != $USER->id) { 01127 $usercontext = get_context_instance(CONTEXT_USER, $this->useridtouseforparentchecks, MUST_EXIST); 01128 if ($DB->record_exists('role_assignments', array('userid' => $USER->id, 'contextid' => $usercontext->id)) 01129 and has_capability('moodle/user:viewdetails', $usercontext)) { 01130 $isparent = true; 01131 } 01132 } 01133 } 01134 01135 if (!$isparent) { 01136 $coursenode->make_active(); 01137 $canviewcourseprofile = false; 01138 break; 01139 } 01140 } 01141 // Add the essentials such as reports etc... 01142 $this->add_course_essentials($coursenode, $course); 01143 if ($this->format_display_course_content($course->format)) { 01144 // Load the course sections 01145 $sections = $this->load_course_sections($course, $coursenode); 01146 } 01147 if (!$coursenode->contains_active_node() && !$coursenode->search_for_active_node()) { 01148 $coursenode->make_active(); 01149 } 01150 break; 01151 case CONTEXT_MODULE : 01152 $course = $this->page->course; 01153 $cm = $this->page->cm; 01154 01155 if ($showcategories && !$ismycourse) { 01156 $this->load_all_categories($course->category, $showcategories); 01157 } 01158 01159 // Load the course associated with the page into the navigation 01160 $coursenode = $this->load_course($course); 01161 01162 // If the course wasn't added then don't try going any further. 01163 if (!$coursenode) { 01164 $canviewcourseprofile = false; 01165 break; 01166 } 01167 01168 // If the user is not enrolled then we only want to show the 01169 // course node and not populate it. 01170 if (!can_access_course($course)) { 01171 $coursenode->make_active(); 01172 $canviewcourseprofile = false; 01173 break; 01174 } 01175 01176 $this->add_course_essentials($coursenode, $course); 01177 01178 // Get section number from $cm (if provided) - we need this 01179 // before loading sections in order to tell it to load this section 01180 // even if it would not normally display (=> it contains only 01181 // a label, which we are now editing) 01182 $sectionnum = isset($cm->sectionnum) ? $cm->sectionnum : 0; 01183 if ($sectionnum) { 01184 // This value has to be stored in a member variable because 01185 // otherwise we would have to pass it through a public API 01186 // to course formats and they would need to change their 01187 // functions to pass it along again... 01188 $this->includesectionnum = $sectionnum; 01189 } else { 01190 $this->includesectionnum = false; 01191 } 01192 01193 // Load the course sections into the page 01194 $sections = $this->load_course_sections($course, $coursenode); 01195 if ($course->id != SITEID) { 01196 // Find the section for the $CM associated with the page and collect 01197 // its section number. 01198 if ($sectionnum) { 01199 $cm->sectionnumber = $sectionnum; 01200 } else { 01201 foreach ($sections as $section) { 01202 if ($section->id == $cm->section) { 01203 $cm->sectionnumber = $section->section; 01204 break; 01205 } 01206 } 01207 } 01208 01209 // Load all of the section activities for the section the cm belongs to. 01210 if (isset($cm->sectionnumber) and !empty($sections[$cm->sectionnumber])) { 01211 list($sectionarray, $activityarray) = $this->generate_sections_and_activities($course); 01212 $activities = $this->load_section_activities($sections[$cm->sectionnumber]->sectionnode, $cm->sectionnumber, $activityarray); 01213 } else { 01214 $activities = array(); 01215 if ($activity = $this->load_stealth_activity($coursenode, get_fast_modinfo($course))) { 01216 // "stealth" activity from unavailable section 01217 $activities[$cm->id] = $activity; 01218 } 01219 } 01220 } else { 01221 $activities = array(); 01222 $activities[$cm->id] = $coursenode->get($cm->id, navigation_node::TYPE_ACTIVITY); 01223 } 01224 if (!empty($activities[$cm->id])) { 01225 // Finally load the cm specific navigaton information 01226 $this->load_activity($cm, $course, $activities[$cm->id]); 01227 // Check if we have an active ndoe 01228 if (!$activities[$cm->id]->contains_active_node() && !$activities[$cm->id]->search_for_active_node()) { 01229 // And make the activity node active. 01230 $activities[$cm->id]->make_active(); 01231 } 01232 } else { 01233 //TODO: something is wrong, what to do? (Skodak) 01234 } 01235 break; 01236 case CONTEXT_USER : 01237 $course = $this->page->course; 01238 if ($showcategories && !$ismycourse) { 01239 $this->load_all_categories($course->category, $showcategories); 01240 } 01241 // Load the course associated with the user into the navigation 01242 $coursenode = $this->load_course($course); 01243 01244 // If the course wasn't added then don't try going any further. 01245 if (!$coursenode) { 01246 $canviewcourseprofile = false; 01247 break; 01248 } 01249 01250 // If the user is not enrolled then we only want to show the 01251 // course node and not populate it. 01252 if (!can_access_course($course)) { 01253 $coursenode->make_active(); 01254 $canviewcourseprofile = false; 01255 break; 01256 } 01257 $this->add_course_essentials($coursenode, $course); 01258 $sections = $this->load_course_sections($course, $coursenode); 01259 break; 01260 } 01261 } else { 01262 // We need to check if the user is viewing a front page module. 01263 // If so then there is potentially more content to load yet for that 01264 // module. 01265 if ($this->page->context->contextlevel == CONTEXT_MODULE) { 01266 $activitynode = $this->rootnodes['site']->get($this->page->cm->id, navigation_node::TYPE_ACTIVITY); 01267 if ($activitynode) { 01268 $this->load_activity($this->page->cm, $this->page->course, $activitynode); 01269 } 01270 } 01271 } 01272 01273 $limit = 20; 01274 if (!empty($CFG->navcourselimit)) { 01275 $limit = $CFG->navcourselimit; 01276 } 01277 if ($showcategories) { 01278 $categories = $this->find_all_of_type(self::TYPE_CATEGORY); 01279 foreach ($categories as &$category) { 01280 if ($category->children->count() >= $limit) { 01281 $url = new moodle_url('/course/category.php', array('id'=>$category->key)); 01282 $category->add(get_string('viewallcourses'), $url, self::TYPE_SETTING); 01283 } 01284 } 01285 } else if ($this->rootnodes['courses']->children->count() >= $limit) { 01286 $this->rootnodes['courses']->add(get_string('viewallcoursescategories'), new moodle_url('/course/index.php'), self::TYPE_SETTING); 01287 } 01288 01289 // Load for the current user 01290 $this->load_for_user(); 01291 if ($this->page->context->contextlevel >= CONTEXT_COURSE && $this->page->context->instanceid != SITEID && $canviewcourseprofile) { 01292 $this->load_for_user(null, true); 01293 } 01294 // Load each extending user into the navigation. 01295 foreach ($this->extendforuser as $user) { 01296 if ($user->id != $USER->id) { 01297 $this->load_for_user($user); 01298 } 01299 } 01300 01301 // Give the local plugins a chance to include some navigation if they want. 01302 foreach (get_list_of_plugins('local') as $plugin) { 01303 if (!file_exists($CFG->dirroot.'/local/'.$plugin.'/lib.php')) { 01304 continue; 01305 } 01306 require_once($CFG->dirroot.'/local/'.$plugin.'/lib.php'); 01307 $function = $plugin.'_extends_navigation'; 01308 if (function_exists($function)) { 01309 $function($this); 01310 } 01311 } 01312 01313 // Remove any empty root nodes 01314 foreach ($this->rootnodes as $node) { 01315 // Dont remove the home node 01316 if ($node->key !== 'home' && !$node->has_children()) { 01317 $node->remove(); 01318 } 01319 } 01320 01321 if (!$this->contains_active_node()) { 01322 $this->search_for_active_node(); 01323 } 01324 01325 // If the user is not logged in modify the navigation structure as detailed 01326 // in {@link http://docs.moodle.org/dev/Navigation_2.0_structure} 01327 if (!isloggedin()) { 01328 $activities = clone($this->rootnodes['site']->children); 01329 $this->rootnodes['site']->remove(); 01330 $children = clone($this->children); 01331 $this->children = new navigation_node_collection(); 01332 foreach ($activities as $child) { 01333 $this->children->add($child); 01334 } 01335 foreach ($children as $child) { 01336 $this->children->add($child); 01337 } 01338 } 01339 return true; 01340 } 01341 01347 protected function show_categories() { 01348 global $CFG, $DB; 01349 if ($this->showcategories === null) { 01350 $this->showcategories = !empty($CFG->navshowcategories) && $DB->count_records('course_categories') > 1; 01351 } 01352 return $this->showcategories; 01353 } 01354 01368 protected function format_display_course_content($format) { 01369 global $CFG; 01370 $formatlib = $CFG->dirroot.'/course/format/'.$format.'/lib.php'; 01371 if (file_exists($formatlib)) { 01372 require_once($formatlib); 01373 $displayfunc = 'callback_'.$format.'_display_content'; 01374 if (function_exists($displayfunc) && !$displayfunc()) { 01375 return $displayfunc(); 01376 } 01377 } 01378 return true; 01379 } 01380 01388 protected function load_all_courses($categoryids=null) { 01389 global $CFG, $DB, $USER; 01390 01391 if ($categoryids !== null) { 01392 if (is_array($categoryids)) { 01393 list ($categoryselect, $params) = $DB->get_in_or_equal($categoryids, SQL_PARAMS_NAMED, 'catid'); 01394 } else { 01395 $categoryselect = '= :categoryid'; 01396 $params = array('categoryid', $categoryids); 01397 } 01398 $params['siteid'] = SITEID; 01399 $categoryselect = ' AND c.category '.$categoryselect; 01400 } else { 01401 $params = array('siteid' => SITEID); 01402 $categoryselect = ''; 01403 } 01404 01405 list($ccselect, $ccjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx'); 01406 list($courseids, $courseparams) = $DB->get_in_or_equal(array_keys($this->addedcourses) + array(SITEID), SQL_PARAMS_NAMED, 'lcourse', false); 01407 $sql = "SELECT c.id, c.sortorder, c.visible, c.fullname, c.shortname, c.category, cat.path AS categorypath $ccselect 01408 FROM {course} c 01409 $ccjoin 01410 LEFT JOIN {course_categories} cat ON cat.id=c.category 01411 WHERE c.id {$courseids} {$categoryselect} 01412 ORDER BY c.sortorder ASC"; 01413 $limit = 20; 01414 if (!empty($CFG->navcourselimit)) { 01415 $limit = $CFG->navcourselimit; 01416 } 01417 $courses = $DB->get_records_sql($sql, $params + $courseparams, 0, $limit); 01418 01419 $coursenodes = array(); 01420 foreach ($courses as $course) { 01421 context_instance_preload($course); 01422 $coursenodes[$course->id] = $this->add_course($course); 01423 } 01424 return $coursenodes; 01425 } 01426 01435 protected function load_all_categories($categoryid = null, $showbasecategories = false) { 01436 global $DB; 01437 01438 // Check if this category has already been loaded 01439 if ($categoryid !== null && array_key_exists($categoryid, $this->addedcategories) && $this->addedcategories[$categoryid]->children->count() > 0) { 01440 return $this->addedcategories[$categoryid]; 01441 } 01442 01443 $coursestoload = array(); 01444 if (empty($categoryid)) { // can be 0 01445 // We are going to load all of the first level categories (categories without parents) 01446 $categories = $DB->get_records('course_categories', array('parent'=>'0'), 'sortorder ASC, id ASC'); 01447 } else if (array_key_exists($categoryid, $this->addedcategories)) { 01448 // The category itself has been loaded already so we just need to ensure its subcategories 01449 // have been loaded 01450 list($sql, $params) = $DB->get_in_or_equal(array_keys($this->addedcategories), SQL_PARAMS_NAMED, 'parent', false); 01451 if ($showbasecategories) { 01452 // We need to include categories with parent = 0 as well 01453 $sql = "SELECT * 01454 FROM {course_categories} cc 01455 WHERE (parent = :categoryid OR parent = 0) AND 01456 parent {$sql} 01457 ORDER BY depth DESC, sortorder ASC, id ASC"; 01458 } else { 01459 $sql = "SELECT * 01460 FROM {course_categories} cc 01461 WHERE parent = :categoryid AND 01462 parent {$sql} 01463 ORDER BY depth DESC, sortorder ASC, id ASC"; 01464 } 01465 $params['categoryid'] = $categoryid; 01466 $categories = $DB->get_records_sql($sql, $params); 01467 if (count($categories) == 0) { 01468 // There are no further categories that require loading. 01469 return; 01470 } 01471 } else { 01472 // This category hasn't been loaded yet so we need to fetch it, work out its category path 01473 // and load this category plus all its parents and subcategories 01474 $category = $DB->get_record('course_categories', array('id' => $categoryid), 'path', MUST_EXIST); 01475 $coursestoload = explode('/', trim($category->path, '/')); 01476 list($select, $params) = $DB->get_in_or_equal($coursestoload); 01477 $select = 'id '.$select.' OR parent '.$select; 01478 if ($showbasecategories) { 01479 $select .= ' OR parent = 0'; 01480 } 01481 $params = array_merge($params, $params); 01482 $categories = $DB->get_records_select('course_categories', $select, $params, 'sortorder'); 01483 } 01484 01485 // Now we have an array of categories we need to add them to the navigation. 01486 while (!empty($categories)) { 01487 $category = reset($categories); 01488 if (array_key_exists($category->id, $this->addedcategories)) { 01489 // Do nothing 01490 } else if ($category->parent == '0') { 01491 $this->add_category($category, $this->rootnodes['courses']); 01492 } else if (array_key_exists($category->parent, $this->addedcategories)) { 01493 $this->add_category($category, $this->addedcategories[$category->parent]); 01494 } else { 01495 // This category isn't in the navigation and niether is it's parent (yet). 01496 // We need to go through the category path and add all of its components in order. 01497 $path = explode('/', trim($category->path, '/')); 01498 foreach ($path as $catid) { 01499 if (!array_key_exists($catid, $this->addedcategories)) { 01500 // This category isn't in the navigation yet so add it. 01501 $subcategory = $categories[$catid]; 01502 if ($subcategory->parent == '0') { 01503 // Yay we have a root category - this likely means we will now be able 01504 // to add categories without problems. 01505 $this->add_category($subcategory, $this->rootnodes['courses']); 01506 } else if (array_key_exists($subcategory->parent, $this->addedcategories)) { 01507 // The parent is in the category (as we'd expect) so add it now. 01508 $this->add_category($subcategory, $this->addedcategories[$subcategory->parent]); 01509 // Remove the category from the categories array. 01510 unset($categories[$catid]); 01511 } else { 01512 // We should never ever arrive here - if we have then there is a bigger 01513 // problem at hand. 01514 throw new coding_exception('Category path order is incorrect and/or there are missing categories'); 01515 } 01516 } 01517 } 01518 } 01519 // Remove the category from the categories array now that we know it has been added. 01520 unset($categories[$category->id]); 01521 } 01522 // Check if there are any categories to load. 01523 if (count($coursestoload) > 0) { 01524 $this->load_all_courses($coursestoload); 01525 } 01526 } 01527 01534 protected function add_category(stdClass $category, navigation_node $parent) { 01535 if (array_key_exists($category->id, $this->addedcategories)) { 01536 return; 01537 } 01538 $url = new moodle_url('/course/category.php', array('id' => $category->id)); 01539 $context = get_context_instance(CONTEXT_COURSECAT, $category->id); 01540 $categoryname = format_string($category->name, true, array('context' => $context)); 01541 $categorynode = $parent->add($categoryname, $url, self::TYPE_CATEGORY, $categoryname, $category->id); 01542 if (empty($category->visible)) { 01543 if (has_capability('moodle/category:viewhiddencategories', get_system_context())) { 01544 $categorynode->hidden = true; 01545 } else { 01546 $categorynode->display = false; 01547 } 01548 } 01549 $this->addedcategories[$category->id] = &$categorynode; 01550 } 01551 01558 protected function load_course(stdClass $course) { 01559 if ($course->id == SITEID) { 01560 return $this->rootnodes['site']; 01561 } else if (array_key_exists($course->id, $this->addedcourses)) { 01562 return $this->addedcourses[$course->id]; 01563 } else { 01564 return $this->add_course($course); 01565 } 01566 } 01567 01582 protected function load_course_sections(stdClass $course, navigation_node $coursenode) { 01583 global $CFG; 01584 $structurefile = $CFG->dirroot.'/course/format/'.$course->format.'/lib.php'; 01585 $structurefunc = 'callback_'.$course->format.'_load_content'; 01586 if (function_exists($structurefunc)) { 01587 return $structurefunc($this, $course, $coursenode); 01588 } else if (file_exists($structurefile)) { 01589 require_once $structurefile; 01590 if (function_exists($structurefunc)) { 01591 return $structurefunc($this, $course, $coursenode); 01592 } else { 01593 return $this->load_generic_course_sections($course, $coursenode); 01594 } 01595 } else { 01596 return $this->load_generic_course_sections($course, $coursenode); 01597 } 01598 } 01599 01608 protected function generate_sections_and_activities(stdClass $course) { 01609 global $CFG; 01610 require_once($CFG->dirroot.'/course/lib.php'); 01611 01612 if (!$this->cache->cached('course_sections_'.$course->id) || !$this->cache->cached('course_activites_'.$course->id)) { 01613 $modinfo = get_fast_modinfo($course); 01614 $sections = array_slice(get_all_sections($course->id), 0, $course->numsections+1, true); 01615 01616 $activities = array(); 01617 01618 foreach ($sections as $key => $section) { 01619 $sections[$key]->hasactivites = false; 01620 if (!array_key_exists($section->section, $modinfo->sections)) { 01621 continue; 01622 } 01623 foreach ($modinfo->sections[$section->section] as $cmid) { 01624 $cm = $modinfo->cms[$cmid]; 01625 if (!$cm->uservisible) { 01626 continue; 01627 } 01628 $activity = new stdClass; 01629 $activity->section = $section->section; 01630 $activity->name = $cm->name; 01631 $activity->icon = $cm->icon; 01632 $activity->iconcomponent = $cm->iconcomponent; 01633 $activity->id = $cm->id; 01634 $activity->hidden = (!$cm->visible); 01635 $activity->modname = $cm->modname; 01636 $activity->nodetype = navigation_node::NODETYPE_LEAF; 01637 $activity->onclick = $cm->get_on_click(); 01638 $url = $cm->get_url(); 01639 if (!$url) { 01640 $activity->url = null; 01641 $activity->display = false; 01642 } else { 01643 $activity->url = $cm->get_url()->out(); 01644 $activity->display = true; 01645 if (self::module_extends_navigation($cm->modname)) { 01646 $activity->nodetype = navigation_node::NODETYPE_BRANCH; 01647 } 01648 } 01649 $activities[$cmid] = $activity; 01650 if ($activity->display) { 01651 $sections[$key]->hasactivites = true; 01652 } 01653 } 01654 } 01655 $this->cache->set('course_sections_'.$course->id, $sections); 01656 $this->cache->set('course_activites_'.$course->id, $activities); 01657 } else { 01658 $sections = $this->cache->{'course_sections_'.$course->id}; 01659 $activities = $this->cache->{'course_activites_'.$course->id}; 01660 } 01661 return array($sections, $activities); 01662 } 01663 01672 public function load_generic_course_sections(stdClass $course, navigation_node $coursenode, $courseformat='unknown') { 01673 global $CFG, $DB, $USER; 01674 require_once($CFG->dirroot.'/course/lib.php'); 01675 01676 list($sections, $activities) = $this->generate_sections_and_activities($course); 01677 01678 $namingfunction = 'callback_'.$courseformat.'_get_section_name'; 01679 $namingfunctionexists = (function_exists($namingfunction)); 01680 01681 $viewhiddensections = has_capability('moodle/course:viewhiddensections', $this->page->context); 01682 01683 $urlfunction = 'callback_'.$courseformat.'_get_section_url'; 01684 if (empty($CFG->navlinkcoursesections) || !function_exists($urlfunction)) { 01685 $urlfunction = null; 01686 } 01687 01688 $keyfunction = 'callback_'.$courseformat.'_request_key'; 01689 $key = course_get_display($course->id); 01690 if (defined('AJAX_SCRIPT') && AJAX_SCRIPT == '0' && function_exists($keyfunction) && $this->page->url->compare(new moodle_url('/course/view.php'), URL_MATCH_BASE)) { 01691 $key = optional_param($keyfunction(), $key, PARAM_INT); 01692 } 01693 01694 $navigationsections = array(); 01695 foreach ($sections as $sectionid => $section) { 01696 $section = clone($section); 01697 if ($course->id == SITEID) { 01698 $this->load_section_activities($coursenode, $section->section, $activities); 01699 } else { 01700 if ((!$viewhiddensections && !$section->visible) || (!$this->showemptysections && 01701 !$section->hasactivites && $this->includesectionnum !== $section->section)) { 01702 continue; 01703 } 01704 if ($namingfunctionexists) { 01705 $sectionname = $namingfunction($course, $section, $sections); 01706 } else { 01707 $sectionname = get_string('section').' '.$section->section; 01708 } 01709 01710 $url = null; 01711 if (!empty($urlfunction)) { 01712 $url = $urlfunction($course->id, $section->section); 01713 } 01714 $sectionnode = $coursenode->add($sectionname, $url, navigation_node::TYPE_SECTION, null, $section->id); 01715 $sectionnode->nodetype = navigation_node::NODETYPE_BRANCH; 01716 $sectionnode->hidden = (!$section->visible); 01717 if ($key != '0' && $section->section != '0' && $section->section == $key && $this->page->context->contextlevel != CONTEXT_MODULE && $section->hasactivites) { 01718 $sectionnode->make_active(); 01719 $this->load_section_activities($sectionnode, $section->section, $activities); 01720 } 01721 $section->sectionnode = $sectionnode; 01722 $navigationsections[$sectionid] = $section; 01723 } 01724 } 01725 return $navigationsections; 01726 } 01738 protected function load_section_activities(navigation_node $sectionnode, $sectionnumber, $activities) { 01739 // A static counter for JS function naming 01740 static $legacyonclickcounter = 0; 01741 01742 if ($activities instanceof course_modinfo) { 01743 debugging('global_navigation::load_section_activities argument 3 should now recieve an array of activites. See that method for an example.', DEBUG_DEVELOPER); 01744 list($sections, $activities) = $this->generate_sections_and_activities($activities->course); 01745 } 01746 01747 $activitynodes = array(); 01748 foreach ($activities as $activity) { 01749 if ($activity->section != $sectionnumber) { 01750 continue; 01751 } 01752 if ($activity->icon) { 01753 $icon = new pix_icon($activity->icon, get_string('modulename', $activity->modname), $activity->iconcomponent); 01754 } else { 01755 $icon = new pix_icon('icon', get_string('modulename', $activity->modname), $activity->modname); 01756 } 01757 01758 // Prepare the default name and url for the node 01759 $activityname = format_string($activity->name, true, array('context' => get_context_instance(CONTEXT_MODULE, $activity->id))); 01760 $action = new moodle_url($activity->url); 01761 01762 // Check if the onclick property is set (puke!) 01763 if (!empty($activity->onclick)) { 01764 // Increment the counter so that we have a unique number. 01765 $legacyonclickcounter++; 01766 // Generate the function name we will use 01767 $functionname = 'legacy_activity_onclick_handler_'.$legacyonclickcounter; 01768 $propogrationhandler = ''; 01769 // Check if we need to cancel propogation. Remember inline onclick 01770 // events would return false if they wanted to prevent propogation and the 01771 // default action. 01772 if (strpos($activity->onclick, 'return false')) { 01773 $propogrationhandler = 'e.halt();'; 01774 } 01775 // Decode the onclick - it has already been encoded for display (puke) 01776 $onclick = htmlspecialchars_decode($activity->onclick); 01777 // Build the JS function the click event will call 01778 $jscode = "function {$functionname}(e) { $propogrationhandler $onclick }"; 01779 $this->page->requires->js_init_code($jscode); 01780 // Override the default url with the new action link 01781 $action = new action_link($action, $activityname, new component_action('click', $functionname)); 01782 } 01783 01784 $activitynode = $sectionnode->add($activityname, $action, navigation_node::TYPE_ACTIVITY, null, $activity->id, $icon); 01785 $activitynode->title(get_string('modulename', $activity->modname)); 01786 $activitynode->hidden = $activity->hidden; 01787 $activitynode->display = $activity->display; 01788 $activitynode->nodetype = $activity->nodetype; 01789 $activitynodes[$activity->id] = $activitynode; 01790 } 01791 01792 return $activitynodes; 01793 } 01800 protected function load_stealth_activity(navigation_node $coursenode, $modinfo) { 01801 if (empty($modinfo->cms[$this->page->cm->id])) { 01802 return null; 01803 } 01804 $cm = $modinfo->cms[$this->page->cm->id]; 01805 if (!$cm->uservisible) { 01806 return null; 01807 } 01808 if ($cm->icon) { 01809 $icon = new pix_icon($cm->icon, get_string('modulename', $cm->modname), $cm->iconcomponent); 01810 } else { 01811 $icon = new pix_icon('icon', get_string('modulename', $cm->modname), $cm->modname); 01812 } 01813 $url = $cm->get_url(); 01814 $activitynode = $coursenode->add(format_string($cm->name), $url, navigation_node::TYPE_ACTIVITY, null, $cm->id, $icon); 01815 $activitynode->title(get_string('modulename', $cm->modname)); 01816 $activitynode->hidden = (!$cm->visible); 01817 if (!$url) { 01818 // Don't show activities that don't have links! 01819 $activitynode->display = false; 01820 } else if (self::module_extends_navigation($cm->modname)) { 01821 $activitynode->nodetype = navigation_node::NODETYPE_BRANCH; 01822 } 01823 return $activitynode; 01824 } 01841 protected function load_activity($cm, stdClass $course, navigation_node $activity) { 01842 global $CFG, $DB; 01843 01844 // make sure we have a $cm from get_fast_modinfo as this contains activity access details 01845 if (!($cm instanceof cm_info)) { 01846 $modinfo = get_fast_modinfo($course); 01847 $cm = $modinfo->get_cm($cm->id); 01848 } 01849 01850 $activity->nodetype = navigation_node::NODETYPE_LEAF; 01851 $activity->make_active(); 01852 $file = $CFG->dirroot.'/mod/'.$cm->modname.'/lib.php'; 01853 $function = $cm->modname.'_extend_navigation'; 01854 01855 if (file_exists($file)) { 01856 require_once($file); 01857 if (function_exists($function)) { 01858 $activtyrecord = $DB->get_record($cm->modname, array('id' => $cm->instance), '*', MUST_EXIST); 01859 $function($activity, $course, $activtyrecord, $cm); 01860 } 01861 } 01862 01863 // Allow the active advanced grading method plugin to append module navigation 01864 $featuresfunc = $cm->modname.'_supports'; 01865 if (function_exists($featuresfunc) && $featuresfunc(FEATURE_ADVANCED_GRADING)) { 01866 require_once($CFG->dirroot.'/grade/grading/lib.php'); 01867 $gradingman = get_grading_manager($cm->context, $cm->modname); 01868 $gradingman->extend_navigation($this, $activity); 01869 } 01870 01871 return $activity->has_children(); 01872 } 01882 protected function load_for_user($user=null, $forceforcontext=false) { 01883 global $DB, $CFG, $USER; 01884 01885 if ($user === null) { 01886 // We can't require login here but if the user isn't logged in we don't 01887 // want to show anything 01888 if (!isloggedin() || isguestuser()) { 01889 return false; 01890 } 01891 $user = $USER; 01892 } else if (!is_object($user)) { 01893 // If the user is not an object then get them from the database 01894 list($select, $join) = context_instance_preload_sql('u.id', CONTEXT_USER, 'ctx'); 01895 $sql = "SELECT u.* $select FROM {user} u $join WHERE u.id = :userid"; 01896 $user = $DB->get_record_sql($sql, array('userid' => (int)$user), MUST_EXIST); 01897 context_instance_preload($user); 01898 } 01899 01900 $iscurrentuser = ($user->id == $USER->id); 01901 01902 $usercontext = get_context_instance(CONTEXT_USER, $user->id); 01903 01904 // Get the course set against the page, by default this will be the site 01905 $course = $this->page->course; 01906 $baseargs = array('id'=>$user->id); 01907 if ($course->id != SITEID && (!$iscurrentuser || $forceforcontext)) { 01908 $coursenode = $this->load_course($course); 01909 $baseargs['course'] = $course->id; 01910 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); 01911 $issitecourse = false; 01912 } else { 01913 // Load all categories and get the context for the system 01914 $coursecontext = get_context_instance(CONTEXT_SYSTEM); 01915 $issitecourse = true; 01916 } 01917 01918 // Create a node to add user information under. 01919 if ($iscurrentuser && !$forceforcontext) { 01920 // If it's the current user the information will go under the profile root node 01921 $usernode = $this->rootnodes['myprofile']; 01922 $course = get_site(); 01923 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); 01924 $issitecourse = true; 01925 } else { 01926 if (!$issitecourse) { 01927 // Not the current user so add it to the participants node for the current course 01928 $usersnode = $coursenode->get('participants', navigation_node::TYPE_CONTAINER); 01929 $userviewurl = new moodle_url('/user/view.php', $baseargs); 01930 } else { 01931 // This is the site so add a users node to the root branch 01932 $usersnode = $this->rootnodes['users']; 01933 if (has_capability('moodle/course:viewparticipants', $coursecontext)) { 01934 $usersnode->action = new moodle_url('/user/index.php', array('id'=>$course->id)); 01935 } 01936 $userviewurl = new moodle_url('/user/profile.php', $baseargs); 01937 } 01938 if (!$usersnode) { 01939 // We should NEVER get here, if the course hasn't been populated 01940 // with a participants node then the navigaiton either wasn't generated 01941 // for it (you are missing a require_login or set_context call) or 01942 // you don't have access.... in the interests of no leaking informatin 01943 // we simply quit... 01944 return false; 01945 } 01946 // Add a branch for the current user 01947 $canseefullname = has_capability('moodle/site:viewfullnames', $coursecontext); 01948 $usernode = $usersnode->add(fullname($user, $canseefullname), $userviewurl, self::TYPE_USER, null, $user->id); 01949 01950 if ($this->page->context->contextlevel == CONTEXT_USER && $user->id == $this->page->context->instanceid) { 01951 $usernode->make_active(); 01952 } 01953 } 01954 01955 // If the user is the current user or has permission to view the details of the requested 01956 // user than add a view profile link. 01957 if ($iscurrentuser || has_capability('moodle/user:viewdetails', $coursecontext) || has_capability('moodle/user:viewdetails', $usercontext)) { 01958 if ($issitecourse || ($iscurrentuser && !$forceforcontext)) { 01959 $usernode->add(get_string('viewprofile'), new moodle_url('/user/profile.php',$baseargs)); 01960 } else { 01961 $usernode->add(get_string('viewprofile'), new moodle_url('/user/view.php',$baseargs)); 01962 } 01963 } 01964 01965 if (!empty($CFG->navadduserpostslinks)) { 01966 // Add nodes for forum posts and discussions if the user can view either or both 01967 // There are no capability checks here as the content of the page is based 01968 // purely on the forums the current user has access too. 01969 $forumtab = $usernode->add(get_string('forumposts', 'forum')); 01970 $forumtab->add(get_string('posts', 'forum'), new moodle_url('/mod/forum/user.php', $baseargs)); 01971 $forumtab->add(get_string('discussions', 'forum'), new moodle_url('/mod/forum/user.php', array_merge($baseargs, array('mode'=>'discussions')))); 01972 } 01973 01974 // Add blog nodes 01975 if (!empty($CFG->bloglevel)) { 01976 if (!$this->cache->cached('userblogoptions'.$user->id)) { 01977 require_once($CFG->dirroot.'/blog/lib.php'); 01978 // Get all options for the user 01979 $options = blog_get_options_for_user($user); 01980 $this->cache->set('userblogoptions'.$user->id, $options); 01981 } else { 01982 $options = $this->cache->{'userblogoptions'.$user->id}; 01983 } 01984 01985 if (count($options) > 0) { 01986 $blogs = $usernode->add(get_string('blogs', 'blog'), null, navigation_node::TYPE_CONTAINER); 01987 foreach ($options as $type => $option) { 01988 if ($type == "rss") { 01989 $blogs->add($option['string'], $option['link'], settings_navigation::TYPE_SETTING, null, null, new pix_icon('i/rss', '')); 01990 } else { 01991 $blogs->add($option['string'], $option['link']); 01992 } 01993 } 01994 } 01995 } 01996 01997 if (!empty($CFG->messaging)) { 01998 $messageargs = null; 01999 if ($USER->id!=$user->id) { 02000 $messageargs = array('id'=>$user->id); 02001 } 02002 $url = new moodle_url('/message/index.php',$messageargs); 02003 $usernode->add(get_string('messages', 'message'), $url, self::TYPE_SETTING, null, 'messages'); 02004 } 02005 02006 $context = get_context_instance(CONTEXT_USER, $USER->id); 02007 if ($iscurrentuser && has_capability('moodle/user:manageownfiles', $context)) { 02008 $url = new moodle_url('/user/files.php'); 02009 $usernode->add(get_string('myfiles'), $url, self::TYPE_SETTING); 02010 } 02011 02012 // Add a node to view the users notes if permitted 02013 if (!empty($CFG->enablenotes) && has_any_capability(array('moodle/notes:manage', 'moodle/notes:view'), $coursecontext)) { 02014 $url = new moodle_url('/notes/index.php',array('user'=>$user->id)); 02015 if ($coursecontext->instanceid) { 02016 $url->param('course', $coursecontext->instanceid); 02017 } 02018 $usernode->add(get_string('notes', 'notes'), $url); 02019 } 02020 02021 // Add reports node 02022 $reporttab = $usernode->add(get_string('activityreports')); 02023 $reports = get_plugin_list_with_function('report', 'extend_navigation_user', 'lib.php'); 02024 foreach ($reports as $reportfunction) { 02025 $reportfunction($reporttab, $user, $course); 02026 } 02027 $anyreport = has_capability('moodle/user:viewuseractivitiesreport', $usercontext); 02028 if ($anyreport || ($course->showreports && $iscurrentuser && $forceforcontext)) { 02029 // Add grade hardcoded grade report if necessary 02030 $gradeaccess = false; 02031 if (has_capability('moodle/grade:viewall', $coursecontext)) { 02032 //ok - can view all course grades 02033 $gradeaccess = true; 02034 } else if ($course->showgrades) { 02035 if ($iscurrentuser && has_capability('moodle/grade:view', $coursecontext)) { 02036 //ok - can view own grades 02037 $gradeaccess = true; 02038 } else if (has_capability('moodle/grade:viewall', $usercontext)) { 02039 // ok - can view grades of this user - parent most probably 02040 $gradeaccess = true; 02041 } else if ($anyreport) { 02042 // ok - can view grades of this user - parent most probably 02043 $gradeaccess = true; 02044 } 02045 } 02046 if ($gradeaccess) { 02047 $reporttab->add(get_string('grade'), new moodle_url('/course/user.php', array('mode'=>'grade', 'id'=>$course->id, 'user'=>$usercontext->instanceid))); 02048 } 02049 } 02050 // Check the number of nodes in the report node... if there are none remove the node 02051 $reporttab->trim_if_empty(); 02052 02053 // If the user is the current user add the repositories for the current user 02054 $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields)); 02055 if ($iscurrentuser) { 02056 if (!$this->cache->cached('contexthasrepos'.$usercontext->id)) { 02057 require_once($CFG->dirroot . '/repository/lib.php'); 02058 $editabletypes = repository::get_editable_types($usercontext); 02059 $haseditabletypes = !empty($editabletypes); 02060 unset($editabletypes); 02061 $this->cache->set('contexthasrepos'.$usercontext->id, $haseditabletypes); 02062 } else { 02063 $haseditabletypes = $this->cache->{'contexthasrepos'.$usercontext->id}; 02064 } 02065 if ($haseditabletypes) { 02066 $usernode->add(get_string('repositories', 'repository'), new moodle_url('/repository/manage_instances.php', array('contextid' => $usercontext->id))); 02067 } 02068 } else if ($course->id == SITEID && has_capability('moodle/user:viewdetails', $usercontext) && (!in_array('mycourses', $hiddenfields) || has_capability('moodle/user:viewhiddendetails', $coursecontext))) { 02069 02070 // Add view grade report is permitted 02071 $reports = get_plugin_list('gradereport'); 02072 arsort($reports); // user is last, we want to test it first 02073 02074 $userscourses = enrol_get_users_courses($user->id); 02075 $userscoursesnode = $usernode->add(get_string('courses')); 02076 02077 foreach ($userscourses as $usercourse) { 02078 $usercoursecontext = get_context_instance(CONTEXT_COURSE, $usercourse->id); 02079 $usercourseshortname = format_string($usercourse->shortname, true, array('context' => $usercoursecontext)); 02080 $usercoursenode = $userscoursesnode->add($usercourseshortname, new moodle_url('/user/view.php', array('id'=>$user->id, 'course'=>$usercourse->id)), self::TYPE_CONTAINER); 02081 02082 $gradeavailable = has_capability('moodle/grade:viewall', $usercoursecontext); 02083 if (!$gradeavailable && !empty($usercourse->showgrades) && is_array($reports) && !empty($reports)) { 02084 foreach ($reports as $plugin => $plugindir) { 02085 if (has_capability('gradereport/'.$plugin.':view', $usercoursecontext)) { 02086 //stop when the first visible plugin is found 02087 $gradeavailable = true; 02088 break; 02089 } 02090 } 02091 } 02092 02093 if ($gradeavailable) { 02094 $url = new moodle_url('/grade/report/index.php', array('id'=>$usercourse->id)); 02095 $usercoursenode->add(get_string('grades'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/grades', '')); 02096 } 02097 02098 // Add a node to view the users notes if permitted 02099 if (!empty($CFG->enablenotes) && has_any_capability(array('moodle/notes:manage', 'moodle/notes:view'), $usercoursecontext)) { 02100 $url = new moodle_url('/notes/index.php',array('user'=>$user->id, 'course'=>$usercourse->id)); 02101 $usercoursenode->add(get_string('notes', 'notes'), $url, self::TYPE_SETTING); 02102 } 02103 02104 if (can_access_course($usercourse, $user->id)) { 02105 $usercoursenode->add(get_string('entercourse'), new moodle_url('/course/view.php', array('id'=>$usercourse->id)), self::TYPE_SETTING, null, null, new pix_icon('i/course', '')); 02106 } 02107 02108 $reporttab = $usercoursenode->add(get_string('activityreports')); 02109 02110 $reports = get_plugin_list_with_function('report', 'extend_navigation_user', 'lib.php'); 02111 foreach ($reports as $reportfunction) { 02112 $reportfunction($reporttab, $user, $usercourse); 02113 } 02114 02115 $reporttab->trim_if_empty(); 02116 } 02117 } 02118 return true; 02119 } 02120 02129 protected static function module_extends_navigation($modname) { 02130 global $CFG; 02131 static $extendingmodules = array(); 02132 if (!array_key_exists($modname, $extendingmodules)) { 02133 $extendingmodules[$modname] = false; 02134 $file = $CFG->dirroot.'/mod/'.$modname.'/lib.php'; 02135 if (file_exists($file)) { 02136 $function = $modname.'_extend_navigation'; 02137 require_once($file); 02138 $extendingmodules[$modname] = (function_exists($function)); 02139 } 02140 } 02141 return $extendingmodules[$modname]; 02142 } 02148 public function extend_for_user($user) { 02149 $this->extendforuser[] = $user; 02150 } 02151 02157 public function get_extending_users() { 02158 return $this->extendforuser; 02159 } 02166 public function add_course(stdClass $course, $forcegeneric = false, $ismycourse = false) { 02167 global $CFG; 02168 02169 // We found the course... we can return it now :) 02170 if (!$forcegeneric && array_key_exists($course->id, $this->addedcourses)) { 02171 return $this->addedcourses[$course->id]; 02172 } 02173 02174 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); 02175 02176 if ($course->id != SITEID && !$course->visible) { 02177 if (is_role_switched($course->id)) { 02178 // user has to be able to access course in order to switch, let's skip the visibility test here 02179 } else if (!has_capability('moodle/course:viewhiddencourses', $coursecontext)) { 02180 return false; 02181 } 02182 } 02183 02184 $issite = ($course->id == SITEID); 02185 $ismycourse = ($ismycourse && !$forcegeneric); 02186 $shortname = format_string($course->shortname, true, array('context' => $coursecontext)); 02187 02188 if ($issite) { 02189 $parent = $this; 02190 $url = null; 02191 if (empty($CFG->usesitenameforsitepages)) { 02192 $shortname = get_string('sitepages'); 02193 } 02194 } else if ($ismycourse) { 02195 $parent = $this->rootnodes['mycourses']; 02196 $url = new moodle_url('/course/view.php', array('id'=>$course->id)); 02197 } else { 02198 $parent = $this->rootnodes['courses']; 02199 $url = new moodle_url('/course/view.php', array('id'=>$course->id)); 02200 } 02201 02202 if (!$ismycourse && !$issite && !empty($course->category)) { 02203 if ($this->show_categories()) { 02204 // We need to load the category structure for this course 02205 $this->load_all_categories($course->category); 02206 } 02207 if (array_key_exists($course->category, $this->addedcategories)) { 02208 $parent = $this->addedcategories[$course->category]; 02209 // This could lead to the course being created so we should check whether it is the case again 02210 if (!$forcegeneric && array_key_exists($course->id, $this->addedcourses)) { 02211 return $this->addedcourses[$course->id]; 02212 } 02213 } 02214 } 02215 02216 $coursenode = $parent->add($shortname, $url, self::TYPE_COURSE, $shortname, $course->id); 02217 $coursenode->nodetype = self::NODETYPE_BRANCH; 02218 $coursenode->hidden = (!$course->visible); 02219 $coursenode->title(format_string($course->fullname, true, array('context' => get_context_instance(CONTEXT_COURSE, $course->id)))); 02220 if (!$forcegeneric) { 02221 $this->addedcourses[$course->id] = &$coursenode; 02222 } 02223 if ($ismycourse && !empty($CFG->navshowallcourses)) { 02224 // We need to add this course to the general courses node as well as the 02225 // my courses node, rerun the function with the kill param 02226 $genericcourse = $this->add_course($course, true); 02227 if ($genericcourse->isactive) { 02228 $genericcourse->make_inactive(); 02229 $genericcourse->collapse = true; 02230 if ($genericcourse->parent && $genericcourse->parent->type == self::TYPE_CATEGORY) { 02231 $parent = $genericcourse->parent; 02232 while ($parent && $parent->type == self::TYPE_CATEGORY) { 02233 $parent->collapse = true; 02234 $parent = $parent->parent; 02235 } 02236 } 02237 } 02238 } 02239 02240 return $coursenode; 02241 } 02251 public function add_course_essentials($coursenode, stdClass $course) { 02252 global $CFG; 02253 02254 if ($course->id == SITEID) { 02255 return $this->add_front_page_course_essentials($coursenode, $course); 02256 } 02257 02258 if ($coursenode == false || !($coursenode instanceof navigation_node) || $coursenode->get('participants', navigation_node::TYPE_CONTAINER)) { 02259 return true; 02260 } 02261 02262 //Participants 02263 if (has_capability('moodle/course:viewparticipants', $this->page->context)) { 02264 $participants = $coursenode->add(get_string('participants'), new moodle_url('/user/index.php?id='.$course->id), self::TYPE_CONTAINER, get_string('participants'), 'participants'); 02265 $currentgroup = groups_get_course_group($course, true); 02266 if ($course->id == SITEID) { 02267 $filterselect = ''; 02268 } else if ($course->id && !$currentgroup) { 02269 $filterselect = $course->id; 02270 } else { 02271 $filterselect = $currentgroup; 02272 } 02273 $filterselect = clean_param($filterselect, PARAM_INT); 02274 if (($CFG->bloglevel == BLOG_GLOBAL_LEVEL or ($CFG->bloglevel == BLOG_SITE_LEVEL and (isloggedin() and !isguestuser()))) 02275 and has_capability('moodle/blog:view', get_context_instance(CONTEXT_SYSTEM))) { 02276 $blogsurls = new moodle_url('/blog/index.php', array('courseid' => $filterselect)); 02277 $participants->add(get_string('blogs','blog'), $blogsurls->out()); 02278 } 02279 if (!empty($CFG->enablenotes) && (has_capability('moodle/notes:manage', $this->page->context) || has_capability('moodle/notes:view', $this->page->context))) { 02280 $participants->add(get_string('notes','notes'), new moodle_url('/notes/index.php', array('filtertype'=>'course', 'filterselect'=>$course->id))); 02281 } 02282 } else if (count($this->extendforuser) > 0 || $this->page->course->id == $course->id) { 02283 $participants = $coursenode->add(get_string('participants'), null, self::TYPE_CONTAINER, get_string('participants'), 'participants'); 02284 } 02285 02286 // View course reports 02287 if (has_capability('moodle/site:viewreports', $this->page->context)) { // basic capability for listing of reports 02288 $reportnav = $coursenode->add(get_string('reports'), null, self::TYPE_CONTAINER, null, null, new pix_icon('i/stats', '')); 02289 $coursereports = get_plugin_list('coursereport'); // deprecated 02290 foreach ($coursereports as $report=>$dir) { 02291 $libfile = $CFG->dirroot.'/course/report/'.$report.'/lib.php'; 02292 if (file_exists($libfile)) { 02293 require_once($libfile); 02294 $reportfunction = $report.'_report_extend_navigation'; 02295 if (function_exists($report.'_report_extend_navigation')) { 02296 $reportfunction($reportnav, $course, $this->page->context); 02297 } 02298 } 02299 } 02300 02301 $reports = get_plugin_list_with_function('report', 'extend_navigation_course', 'lib.php'); 02302 foreach ($reports as $reportfunction) { 02303 $reportfunction($reportnav, $course, $this->page->context); 02304 } 02305 } 02306 return true; 02307 } 02319 public function add_front_page_course_essentials(navigation_node $coursenode, stdClass $course) { 02320 global $CFG; 02321 02322 if ($coursenode == false || $coursenode->get('frontpageloaded', navigation_node::TYPE_CUSTOM)) { 02323 return true; 02324 } 02325 02326 // Hidden node that we use to determine if the front page navigation is loaded. 02327 // This required as there are not other guaranteed nodes that may be loaded. 02328 $coursenode->add('frontpageloaded', null, self::TYPE_CUSTOM, null, 'frontpageloaded')->display = false; 02329 02330 //Participants 02331 if (has_capability('moodle/course:viewparticipants', get_system_context())) { 02332 $coursenode->add(get_string('participants'), new moodle_url('/user/index.php?id='.$course->id), self::TYPE_CUSTOM, get_string('participants'), 'participants'); 02333 } 02334 02335 $filterselect = 0; 02336 02337 // Blogs 02338 if (!empty($CFG->bloglevel) 02339 and ($CFG->bloglevel == BLOG_GLOBAL_LEVEL or ($CFG->bloglevel == BLOG_SITE_LEVEL and (isloggedin() and !isguestuser()))) 02340 and has_capability('moodle/blog:view', get_context_instance(CONTEXT_SYSTEM))) { 02341 $blogsurls = new moodle_url('/blog/index.php', array('courseid' => $filterselect)); 02342 $coursenode->add(get_string('blogs','blog'), $blogsurls->out()); 02343 } 02344 02345 // Notes 02346 if (!empty($CFG->enablenotes) && (has_capability('moodle/notes:manage', $this->page->context) || has_capability('moodle/notes:view', $this->page->context))) { 02347 $coursenode->add(get_string('notes','notes'), new moodle_url('/notes/index.php', array('filtertype'=>'course', 'filterselect'=>$filterselect))); 02348 } 02349 02350 // Tags 02351 if (!empty($CFG->usetags) && isloggedin()) { 02352 $coursenode->add(get_string('tags', 'tag'), new moodle_url('/tag/search.php')); 02353 } 02354 02355 if (isloggedin()) { 02356 // Calendar 02357 $calendarurl = new moodle_url('/calendar/view.php', array('view' => 'month')); 02358 $coursenode->add(get_string('calendar', 'calendar'), $calendarurl, self::TYPE_CUSTOM, null, 'calendar'); 02359 } 02360 02361 // View course reports 02362 if (has_capability('moodle/site:viewreports', $this->page->context)) { // basic capability for listing of reports 02363 $reportnav = $coursenode->add(get_string('reports'), null, self::TYPE_CONTAINER, null, null, new pix_icon('i/stats', '')); 02364 $coursereports = get_plugin_list('coursereport'); // deprecated 02365 foreach ($coursereports as $report=>$dir) { 02366 $libfile = $CFG->dirroot.'/course/report/'.$report.'/lib.php'; 02367 if (file_exists($libfile)) { 02368 require_once($libfile); 02369 $reportfunction = $report.'_report_extend_navigation'; 02370 if (function_exists($report.'_report_extend_navigation')) { 02371 $reportfunction($reportnav, $course, $this->page->context); 02372 } 02373 } 02374 } 02375 02376 $reports = get_plugin_list_with_function('report', 'extend_navigation_course', 'lib.php'); 02377 foreach ($reports as $reportfunction) { 02378 $reportfunction($reportnav, $course, $this->page->context); 02379 } 02380 } 02381 return true; 02382 } 02383 02387 public function clear_cache() { 02388 $this->cache->clear(); 02389 } 02390 02404 public function set_expansion_limit($type) { 02405 $nodes = $this->find_all_of_type($type); 02406 foreach ($nodes as &$node) { 02407 // We need to generate the full site node 02408 if ($type == self::TYPE_COURSE && $node->key == SITEID) { 02409 continue; 02410 } 02411 foreach ($node->children as &$child) { 02412 // We still want to show course reports and participants containers 02413 // or there will be navigation missing. 02414 if ($type == self::TYPE_COURSE && $child->type === self::TYPE_CONTAINER) { 02415 continue; 02416 } 02417 $child->display = false; 02418 } 02419 } 02420 return true; 02421 } 02437 public function get($key, $type = null) { 02438 if (!$this->initialised) { 02439 $this->initialise(); 02440 } 02441 return parent::get($key, $type); 02442 } 02443 02461 public function find($key, $type) { 02462 if (!$this->initialised) { 02463 $this->initialise(); 02464 } 02465 return parent::find($key, $type); 02466 } 02467 } 02468 02485 class global_navigation_for_ajax extends global_navigation { 02486 02487 protected $branchtype; 02488 protected $instanceid; 02489 02491 protected $expandable = array(); 02492 02496 public function __construct($page, $branchtype, $id) { 02497 $this->page = $page; 02498 $this->cache = new navigation_cache(NAVIGATION_CACHE_NAME); 02499 $this->children = new navigation_node_collection(); 02500 $this->branchtype = $branchtype; 02501 $this->instanceid = $id; 02502 $this->initialise(); 02503 } 02509 public function initialise() { 02510 global $CFG, $DB, $SITE; 02511 02512 if ($this->initialised || during_initial_install()) { 02513 return $this->expandable; 02514 } 02515 $this->initialised = true; 02516 02517 $this->rootnodes = array(); 02518 $this->rootnodes['site'] = $this->add_course($SITE); 02519 $this->rootnodes['courses'] = $this->add(get_string('courses'), null, self::TYPE_ROOTNODE, null, 'courses'); 02520 02521 // Branchtype will be one of navigation_node::TYPE_* 02522 switch ($this->branchtype) { 02523 case self::TYPE_CATEGORY : 02524 $this->load_all_categories($this->instanceid); 02525 $limit = 20; 02526 if (!empty($CFG->navcourselimit)) { 02527 $limit = (int)$CFG->navcourselimit; 02528 } 02529 $courses = $DB->get_records('course', array('category' => $this->instanceid), 'sortorder','*', 0, $limit); 02530 foreach ($courses as $course) { 02531 $this->add_course($course); 02532 } 02533 break; 02534 case self::TYPE_COURSE : 02535 $course = $DB->get_record('course', array('id' => $this->instanceid), '*', MUST_EXIST); 02536 require_course_login($course); 02537 $this->page->set_context(get_context_instance(CONTEXT_COURSE, $course->id)); 02538 $coursenode = $this->add_course($course); 02539 $this->add_course_essentials($coursenode, $course); 02540 if ($this->format_display_course_content($course->format)) { 02541 $this->load_course_sections($course, $coursenode); 02542 } 02543 break; 02544 case self::TYPE_SECTION : 02545 $sql = 'SELECT c.*, cs.section AS sectionnumber 02546 FROM {course} c 02547 LEFT JOIN {course_sections} cs ON cs.course = c.id 02548 WHERE cs.id = ?'; 02549 $course = $DB->get_record_sql($sql, array($this->instanceid), MUST_EXIST); 02550 require_course_login($course); 02551 $this->page->set_context(get_context_instance(CONTEXT_COURSE, $course->id)); 02552 $coursenode = $this->add_course($course); 02553 $this->add_course_essentials($coursenode, $course); 02554 $sections = $this->load_course_sections($course, $coursenode); 02555 list($sectionarray, $activities) = $this->generate_sections_and_activities($course); 02556 $this->load_section_activities($sections[$course->sectionnumber]->sectionnode, $course->sectionnumber, $activities); 02557 break; 02558 case self::TYPE_ACTIVITY : 02559 $sql = "SELECT c.* 02560 FROM {course} c 02561 JOIN {course_modules} cm ON cm.course = c.id 02562 WHERE cm.id = :cmid"; 02563 $params = array('cmid' => $this->instanceid); 02564 $course = $DB->get_record_sql($sql, $params, MUST_EXIST); 02565 $modinfo = get_fast_modinfo($course); 02566 $cm = $modinfo->get_cm($this->instanceid); 02567 require_course_login($course, true, $cm); 02568 $this->page->set_context(get_context_instance(CONTEXT_MODULE, $cm->id)); 02569 $coursenode = $this->load_course($course); 02570 if ($course->id == SITEID) { 02571 $modulenode = $this->load_activity($cm, $course, $coursenode->find($cm->id, self::TYPE_ACTIVITY)); 02572 } else { 02573 $sections = $this->load_course_sections($course, $coursenode); 02574 list($sectionarray, $activities) = $this->generate_sections_and_activities($course); 02575 $activities = $this->load_section_activities($sections[$cm->sectionnum]->sectionnode, $cm->sectionnum, $activities); 02576 $modulenode = $this->load_activity($cm, $course, $activities[$cm->id]); 02577 } 02578 break; 02579 default: 02580 throw new Exception('Unknown type'); 02581 return $this->expandable; 02582 } 02583 02584 if ($this->page->context->contextlevel == CONTEXT_COURSE && $this->page->context->instanceid != SITEID) { 02585 $this->load_for_user(null, true); 02586 } 02587 02588 $this->find_expandable($this->expandable); 02589 return $this->expandable; 02590 } 02591 02596 public function get_expandable() { 02597 return $this->expandable; 02598 } 02599 } 02600 02612 class navbar extends navigation_node { 02614 protected $initialised = false; 02616 protected $keys = array(); 02618 protected $content = null; 02620 protected $page; 02622 protected $ignoreactive = false; 02624 protected $duringinstall = false; 02626 protected $hasitems = false; 02628 protected $items; 02630 public $children = array(); 02632 public $includesettingsbase = false; 02638 public function __construct(moodle_page $page) { 02639 global $CFG; 02640 if (during_initial_install()) { 02641 $this->duringinstall = true; 02642 return false; 02643 } 02644 $this->page = $page; 02645 $this->text = get_string('home'); 02646 $this->shorttext = get_string('home'); 02647 $this->action = new moodle_url($CFG->wwwroot); 02648 $this->nodetype = self::NODETYPE_BRANCH; 02649 $this->type = self::TYPE_SYSTEM; 02650 } 02651 02657 public function has_items() { 02658 if ($this->duringinstall) { 02659 return false; 02660 } else if ($this->hasitems !== false) { 02661 return true; 02662 } 02663 $this->page->navigation->initialise($this->page); 02664 02665 $activenodefound = ($this->page->navigation->contains_active_node() || 02666 $this->page->settingsnav->contains_active_node()); 02667 02668 $outcome = (count($this->children)>0 || (!$this->ignoreactive && $activenodefound)); 02669 $this->hasitems = $outcome; 02670 return $outcome; 02671 } 02672 02678 public function ignore_active($setting=true) { 02679 $this->ignoreactive = ($setting); 02680 } 02681 public function get($key, $type = null) { 02682 foreach ($this->children as &$child) { 02683 if ($child->key === $key && ($type == null || $type == $child->type)) { 02684 return $child; 02685 } 02686 } 02687 return false; 02688 } 02694 public function get_items() { 02695 $items = array(); 02696 // Make sure that navigation is initialised 02697 if (!$this->has_items()) { 02698 return $items; 02699 } 02700 if ($this->items !== null) { 02701 return $this->items; 02702 } 02703 02704 if (count($this->children) > 0) { 02705 // Add the custom children 02706 $items = array_reverse($this->children); 02707 } 02708 02709 $navigationactivenode = $this->page->navigation->find_active_node(); 02710 $settingsactivenode = $this->page->settingsnav->find_active_node(); 02711 02712 // Check if navigation contains the active node 02713 if (!$this->ignoreactive) { 02714 02715 if ($navigationactivenode && $settingsactivenode) { 02716 // Parse a combined navigation tree 02717 while ($settingsactivenode && $settingsactivenode->parent !== null) { 02718 if (!$settingsactivenode->mainnavonly) { 02719 $items[] = $settingsactivenode; 02720 } 02721 $settingsactivenode = $settingsactivenode->parent; 02722 } 02723 if (!$this->includesettingsbase) { 02724 // Removes the first node from the settings (root node) from the list 02725 array_pop($items); 02726 } 02727 while ($navigationactivenode && $navigationactivenode->parent !== null) { 02728 if (!$navigationactivenode->mainnavonly) { 02729 $items[] = $navigationactivenode; 02730 } 02731 $navigationactivenode = $navigationactivenode->parent; 02732 } 02733 } else if ($navigationactivenode) { 02734 // Parse the navigation tree to get the active node 02735 while ($navigationactivenode && $navigationactivenode->parent !== null) { 02736 if (!$navigationactivenode->mainnavonly) { 02737 $items[] = $navigationactivenode; 02738 } 02739 $navigationactivenode = $navigationactivenode->parent; 02740 } 02741 } else if ($settingsactivenode) { 02742 // Parse the settings navigation to get the active node 02743 while ($settingsactivenode && $settingsactivenode->parent !== null) { 02744 if (!$settingsactivenode->mainnavonly) { 02745 $items[] = $settingsactivenode; 02746 } 02747 $settingsactivenode = $settingsactivenode->parent; 02748 } 02749 } 02750 } 02751 02752 $items[] = new navigation_node(array( 02753 'text'=>$this->page->navigation->text, 02754 'shorttext'=>$this->page->navigation->shorttext, 02755 'key'=>$this->page->navigation->key, 02756 'action'=>$this->page->navigation->action 02757 )); 02758 02759 $this->items = array_reverse($items); 02760 return $this->items; 02761 } 02762 02778 public function add($text, $action=null, $type=self::TYPE_CUSTOM, $shorttext=null, $key=null, pix_icon $icon=null) { 02779 if ($this->content !== null) { 02780 debugging('Nav bar items must be printed before $OUTPUT->header() has been called', DEBUG_DEVELOPER); 02781 } 02782 02783 // Properties array used when creating the new navigation node 02784 $itemarray = array( 02785 'text' => $text, 02786 'type' => $type 02787 ); 02788 // Set the action if one was provided 02789 if ($action!==null) { 02790 $itemarray['action'] = $action; 02791 } 02792 // Set the shorttext if one was provided 02793 if ($shorttext!==null) { 02794 $itemarray['shorttext'] = $shorttext; 02795 } 02796 // Set the icon if one was provided 02797 if ($icon!==null) { 02798 $itemarray['icon'] = $icon; 02799 } 02800 // Default the key to the number of children if not provided 02801 if ($key === null) { 02802 $key = count($this->children); 02803 } 02804 // Set the key 02805 $itemarray['key'] = $key; 02806 // Set the parent to this node 02807 $itemarray['parent'] = $this; 02808 // Add the child using the navigation_node_collections add method 02809 $this->children[] = new navigation_node($itemarray); 02810 return $this; 02811 } 02812 } 02813 02825 class settings_navigation extends navigation_node { 02827 protected $context; 02829 protected $page; 02831 protected $adminsection; 02833 protected $initialised = false; 02835 protected $userstoextendfor = array(); 02837 protected $cache; 02838 02844 public function __construct(moodle_page &$page) { 02845 if (during_initial_install()) { 02846 return false; 02847 } 02848 $this->page = $page; 02849 // Initialise the main navigation. It is most important that this is done 02850 // before we try anything 02851 $this->page->navigation->initialise(); 02852 // Initialise the navigation cache 02853 $this->cache = new navigation_cache(NAVIGATION_CACHE_NAME); 02854 $this->children = new navigation_node_collection(); 02855 } 02863 public function initialise() { 02864 global $DB, $SESSION; 02865 02866 if (during_initial_install()) { 02867 return false; 02868 } else if ($this->initialised) { 02869 return true; 02870 } 02871 $this->id = 'settingsnav'; 02872 $this->context = $this->page->context; 02873 02874 $context = $this->context; 02875 if ($context->contextlevel == CONTEXT_BLOCK) { 02876 $this->load_block_settings(); 02877 $context = $context->get_parent_context(); 02878 } 02879 02880 switch ($context->contextlevel) { 02881 case CONTEXT_SYSTEM: 02882 if ($this->page->url->compare(new moodle_url('/admin/settings.php', array('section'=>'frontpagesettings')))) { 02883 $this->load_front_page_settings(($context->id == $this->context->id)); 02884 } 02885 break; 02886 case CONTEXT_COURSECAT: 02887 $this->load_category_settings(); 02888 break; 02889 case CONTEXT_COURSE: 02890 if ($this->page->course->id != SITEID) { 02891 $this->load_course_settings(($context->id == $this->context->id)); 02892 } else { 02893 $this->load_front_page_settings(($context->id == $this->context->id)); 02894 } 02895 break; 02896 case CONTEXT_MODULE: 02897 $this->load_module_settings(); 02898 $this->load_course_settings(); 02899 break; 02900 case CONTEXT_USER: 02901 if ($this->page->course->id != SITEID) { 02902 $this->load_course_settings(); 02903 } 02904 break; 02905 } 02906 02907 $settings = $this->load_user_settings($this->page->course->id); 02908 02909 if (isloggedin() && !isguestuser() && (!property_exists($SESSION, 'load_navigation_admin') || $SESSION->load_navigation_admin)) { 02910 $admin = $this->load_administration_settings(); 02911 $SESSION->load_navigation_admin = ($admin->has_children()); 02912 } else { 02913 $admin = false; 02914 } 02915 02916 if ($context->contextlevel == CONTEXT_SYSTEM && $admin) { 02917 $admin->force_open(); 02918 } else if ($context->contextlevel == CONTEXT_USER && $settings) { 02919 $settings->force_open(); 02920 } 02921 02922 // Check if the user is currently logged in as another user 02923 if (session_is_loggedinas()) { 02924 // Get the actual user, we need this so we can display an informative return link 02925 $realuser = session_get_realuser(); 02926 // Add the informative return to original user link 02927 $url = new moodle_url('/course/loginas.php',array('id'=>$this->page->course->id, 'return'=>1,'sesskey'=>sesskey())); 02928 $this->add(get_string('returntooriginaluser', 'moodle', fullname($realuser, true)), $url, self::TYPE_SETTING, null, null, new pix_icon('t/left', '')); 02929 } 02930 02931 foreach ($this->children as $key=>$node) { 02932 if ($node->nodetype != self::NODETYPE_BRANCH || $node->children->count()===0) { 02933 $node->remove(); 02934 } 02935 } 02936 $this->initialised = true; 02937 } 02953 public function add($text, $url=null, $type=null, $shorttext=null, $key=null, pix_icon $icon=null) { 02954 $node = parent::add($text, $url, $type, $shorttext, $key, $icon); 02955 $node->add_class('root_node'); 02956 return $node; 02957 } 02958 02971 public function prepend($text, $url=null, $type=null, $shorttext=null, $key=null, pix_icon $icon=null) { 02972 $children = $this->children; 02973 $childrenclass = get_class($children); 02974 $this->children = new $childrenclass; 02975 $node = $this->add($text, $url, $type, $shorttext, $key, $icon); 02976 foreach ($children as $child) { 02977 $this->children->add($child); 02978 } 02979 return $node; 02980 } 02992 protected function load_administration_settings(navigation_node $referencebranch=null, part_of_admin_tree $adminbranch=null) { 02993 global $CFG; 02994 02995 // Check if we are just starting to generate this navigation. 02996 if ($referencebranch === null) { 02997 02998 // Require the admin lib then get an admin structure 02999 if (!function_exists('admin_get_root')) { 03000 require_once($CFG->dirroot.'/lib/adminlib.php'); 03001 } 03002 $adminroot = admin_get_root(false, false); 03003 // This is the active section identifier 03004 $this->adminsection = $this->page->url->param('section'); 03005 03006 // Disable the navigation from automatically finding the active node 03007 navigation_node::$autofindactive = false; 03008 $referencebranch = $this->add(get_string('administrationsite'), null, self::TYPE_SETTING, null, 'root'); 03009 foreach ($adminroot->children as $adminbranch) { 03010 $this->load_administration_settings($referencebranch, $adminbranch); 03011 } 03012 navigation_node::$autofindactive = true; 03013 03014 // Use the admin structure to locate the active page 03015 if (!$this->contains_active_node() && $current = $adminroot->locate($this->adminsection, true)) { 03016 $currentnode = $this; 03017 while (($pathkey = array_pop($current->path))!==null && $currentnode) { 03018 $currentnode = $currentnode->get($pathkey); 03019 } 03020 if ($currentnode) { 03021 $currentnode->make_active(); 03022 } 03023 } else { 03024 $this->scan_for_active_node($referencebranch); 03025 } 03026 return $referencebranch; 03027 } else if ($adminbranch->check_access()) { 03028 // We have a reference branch that we can access and is not hidden `hurrah` 03029 // Now we need to display it and any children it may have 03030 $url = null; 03031 $icon = null; 03032 if ($adminbranch instanceof admin_settingpage) { 03033 $url = new moodle_url('/admin/settings.php', array('section'=>$adminbranch->name)); 03034 } else if ($adminbranch instanceof admin_externalpage) { 03035 $url = $adminbranch->url; 03036 } 03037 03038 // Add the branch 03039 $reference = $referencebranch->add($adminbranch->visiblename, $url, self::TYPE_SETTING, null, $adminbranch->name, $icon); 03040 03041 if ($adminbranch->is_hidden()) { 03042 if (($adminbranch instanceof admin_externalpage || $adminbranch instanceof admin_settingpage) && $adminbranch->name == $this->adminsection) { 03043 $reference->add_class('hidden'); 03044 } else { 03045 $reference->display = false; 03046 } 03047 } 03048 03049 // Check if we are generating the admin notifications and whether notificiations exist 03050 if ($adminbranch->name === 'adminnotifications' && admin_critical_warnings_present()) { 03051 $reference->add_class('criticalnotification'); 03052 } 03053 // Check if this branch has children 03054 if ($reference && isset($adminbranch->children) && is_array($adminbranch->children) && count($adminbranch->children)>0) { 03055 foreach ($adminbranch->children as $branch) { 03056 // Generate the child branches as well now using this branch as the reference 03057 $this->load_administration_settings($reference, $branch); 03058 } 03059 } else { 03060 $reference->icon = new pix_icon('i/settings', ''); 03061 } 03062 } 03063 } 03064 03070 protected function scan_for_active_node(navigation_node $node) { 03071 if (!$node->check_if_active() && $node->children->count()>0) { 03072 foreach ($node->children as &$child) { 03073 $this->scan_for_active_node($child); 03074 } 03075 } 03076 } 03077 03085 protected function get_by_path(array $path) { 03086 $node = $this->get(array_shift($path)); 03087 foreach ($path as $key) { 03088 $node->get($key); 03089 } 03090 return $node; 03091 } 03092 03098 protected function get_course_modules($course) { 03099 global $CFG; 03100 $mods = $modnames = $modnamesplural = $modnamesused = array(); 03101 // This function is included when we include course/lib.php at the top 03102 // of this file 03103 get_all_mods($course->id, $mods, $modnames, $modnamesplural, $modnamesused); 03104 $resources = array(); 03105 $activities = array(); 03106 foreach($modnames as $modname=>$modnamestr) { 03107 if (!course_allowed_module($course, $modname)) { 03108 continue; 03109 } 03110 03111 $libfile = "$CFG->dirroot/mod/$modname/lib.php"; 03112 if (!file_exists($libfile)) { 03113 continue; 03114 } 03115 include_once($libfile); 03116 $gettypesfunc = $modname.'_get_types'; 03117 if (function_exists($gettypesfunc)) { 03118 $types = $gettypesfunc(); 03119 foreach($types as $type) { 03120 if (!isset($type->modclass) || !isset($type->typestr)) { 03121 debugging('Incorrect activity type in '.$modname); 03122 continue; 03123 } 03124 if ($type->modclass == MOD_CLASS_RESOURCE) { 03125 $resources[html_entity_decode($type->type)] = $type->typestr; 03126 } else { 03127 $activities[html_entity_decode($type->type)] = $type->typestr; 03128 } 03129 } 03130 } else { 03131 $archetype = plugin_supports('mod', $modname, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER); 03132 if ($archetype == MOD_ARCHETYPE_RESOURCE) { 03133 $resources[$modname] = $modnamestr; 03134 } else { 03135 // all other archetypes are considered activity 03136 $activities[$modname] = $modnamestr; 03137 } 03138 } 03139 } 03140 return array($resources, $activities); 03141 } 03142 03149 protected function load_course_settings($forceopen = false) { 03150 global $CFG; 03151 03152 $course = $this->page->course; 03153 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); 03154 03155 // note: do not test if enrolled or viewing here because we need the enrol link in Course administration section 03156 03157 $coursenode = $this->add(get_string('courseadministration'), null, self::TYPE_COURSE, null, 'courseadmin'); 03158 if ($forceopen) { 03159 $coursenode->force_open(); 03160 } 03161 03162 if (has_capability('moodle/course:update', $coursecontext)) { 03163 // Add the turn on/off settings 03164 $url = new moodle_url('/course/view.php', array('id'=>$course->id, 'sesskey'=>sesskey())); 03165 if ($this->page->user_is_editing()) { 03166 $url->param('edit', 'off'); 03167 $editstring = get_string('turneditingoff'); 03168 } else { 03169 $url->param('edit', 'on'); 03170 $editstring = get_string('turneditingon'); 03171 } 03172 $coursenode->add($editstring, $url, self::TYPE_SETTING, null, null, new pix_icon('i/edit', '')); 03173 03174 if ($this->page->user_is_editing()) { 03175 // Removed as per MDL-22732 03176 // $this->add_course_editing_links($course); 03177 } 03178 03179 // Add the course settings link 03180 $url = new moodle_url('/course/edit.php', array('id'=>$course->id)); 03181 $coursenode->add(get_string('editsettings'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/settings', '')); 03182 03183 // Add the course completion settings link 03184 if ($CFG->enablecompletion && $course->enablecompletion) { 03185 $url = new moodle_url('/course/completion.php', array('id'=>$course->id)); 03186 $coursenode->add(get_string('completion', 'completion'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/settings', '')); 03187 } 03188 } 03189 03190 // add enrol nodes 03191 enrol_add_course_navigation($coursenode, $course); 03192 03193 // Manage filters 03194 if (has_capability('moodle/filter:manage', $coursecontext) && count(filter_get_available_in_context($coursecontext))>0) { 03195 $url = new moodle_url('/filter/manage.php', array('contextid'=>$coursecontext->id)); 03196 $coursenode->add(get_string('filters', 'admin'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/filter', '')); 03197 } 03198 03199 // Add view grade report is permitted 03200 $reportavailable = false; 03201 if (has_capability('moodle/grade:viewall', $coursecontext)) { 03202 $reportavailable = true; 03203 } else if (!empty($course->showgrades)) { 03204 $reports = get_plugin_list('gradereport'); 03205 if (is_array($reports) && count($reports)>0) { // Get all installed reports 03206 arsort($reports); // user is last, we want to test it first 03207 foreach ($reports as $plugin => $plugindir) { 03208 if (has_capability('gradereport/'.$plugin.':view', $coursecontext)) { 03209 //stop when the first visible plugin is found 03210 $reportavailable = true; 03211 break; 03212 } 03213 } 03214 } 03215 } 03216 if ($reportavailable) { 03217 $url = new moodle_url('/grade/report/index.php', array('id'=>$course->id)); 03218 $gradenode = $coursenode->add(get_string('grades'), $url, self::TYPE_SETTING, null, 'grades', new pix_icon('i/grades', '')); 03219 } 03220 03221 // Add outcome if permitted 03222 if (!empty($CFG->enableoutcomes) && has_capability('moodle/course:update', $coursecontext)) { 03223 $url = new moodle_url('/grade/edit/outcome/course.php', array('id'=>$course->id)); 03224 $coursenode->add(get_string('outcomes', 'grades'), $url, self::TYPE_SETTING, null, 'outcomes', new pix_icon('i/outcomes', '')); 03225 } 03226 03227 // Backup this course 03228 if (has_capability('moodle/backup:backupcourse', $coursecontext)) { 03229 $url = new moodle_url('/backup/backup.php', array('id'=>$course->id)); 03230 $coursenode->add(get_string('backup'), $url, self::TYPE_SETTING, null, 'backup', new pix_icon('i/backup', '')); 03231 } 03232 03233 // Restore to this course 03234 if (has_capability('moodle/restore:restorecourse', $coursecontext)) { 03235 $url = new moodle_url('/backup/restorefile.php', array('contextid'=>$coursecontext->id)); 03236 $coursenode->add(get_string('restore'), $url, self::TYPE_SETTING, null, 'restore', new pix_icon('i/restore', '')); 03237 } 03238 03239 // Import data from other courses 03240 if (has_capability('moodle/restore:restoretargetimport', $coursecontext)) { 03241 $url = new moodle_url('/backup/import.php', array('id'=>$course->id)); 03242 $coursenode->add(get_string('import'), $url, self::TYPE_SETTING, null, 'import', new pix_icon('i/restore', '')); 03243 } 03244 03245 // Publish course on a hub 03246 if (has_capability('moodle/course:publish', $coursecontext)) { 03247 $url = new moodle_url('/course/publish/index.php', array('id'=>$course->id)); 03248 $coursenode->add(get_string('publish'), $url, self::TYPE_SETTING, null, 'publish', new pix_icon('i/publish', '')); 03249 } 03250 03251 // Reset this course 03252 if (has_capability('moodle/course:reset', $coursecontext)) { 03253 $url = new moodle_url('/course/reset.php', array('id'=>$course->id)); 03254 $coursenode->add(get_string('reset'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/return', '')); 03255 } 03256 03257 // Questions 03258 require_once($CFG->libdir . '/questionlib.php'); 03259 question_extend_settings_navigation($coursenode, $coursecontext)->trim_if_empty(); 03260 03261 if (has_capability('moodle/course:update', $coursecontext)) { 03262 // Repository Instances 03263 if (!$this->cache->cached('contexthasrepos'.$coursecontext->id)) { 03264 require_once($CFG->dirroot . '/repository/lib.php'); 03265 $editabletypes = repository::get_editable_types($coursecontext); 03266 $haseditabletypes = !empty($editabletypes); 03267 unset($editabletypes); 03268 $this->cache->set('contexthasrepos'.$coursecontext->id, $haseditabletypes); 03269 } else { 03270 $haseditabletypes = $this->cache->{'contexthasrepos'.$coursecontext->id}; 03271 } 03272 if ($haseditabletypes) { 03273 $url = new moodle_url('/repository/manage_instances.php', array('contextid' => $coursecontext->id)); 03274 $coursenode->add(get_string('repositories'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/repository', '')); 03275 } 03276 } 03277 03278 // Manage files 03279 if ($course->legacyfiles == 2 and has_capability('moodle/course:managefiles', $coursecontext)) { 03280 // hidden in new courses and courses where legacy files were turned off 03281 $url = new moodle_url('/files/index.php', array('contextid'=>$coursecontext->id)); 03282 $coursenode->add(get_string('courselegacyfiles'), $url, self::TYPE_SETTING, null, 'coursefiles', new pix_icon('i/files', '')); 03283 } 03284 03285 // Switch roles 03286 $roles = array(); 03287 $assumedrole = $this->in_alternative_role(); 03288 if ($assumedrole !== false) { 03289 $roles[0] = get_string('switchrolereturn'); 03290 } 03291 if (has_capability('moodle/role:switchroles', $coursecontext)) { 03292 $availableroles = get_switchable_roles($coursecontext); 03293 if (is_array($availableroles)) { 03294 foreach ($availableroles as $key=>$role) { 03295 if ($assumedrole == (int)$key) { 03296 continue; 03297 } 03298 $roles[$key] = $role; 03299 } 03300 } 03301 } 03302 if (is_array($roles) && count($roles)>0) { 03303 $switchroles = $this->add(get_string('switchroleto')); 03304 if ((count($roles)==1 && array_key_exists(0, $roles))|| $assumedrole!==false) { 03305 $switchroles->force_open(); 03306 } 03307 $returnurl = $this->page->url; 03308 $returnurl->param('sesskey', sesskey()); 03309 foreach ($roles as $key => $name) { 03310 $url = new moodle_url('/course/switchrole.php', array('id'=>$course->id,'sesskey'=>sesskey(), 'switchrole'=>$key, 'returnurl'=>$returnurl->out(false))); 03311 $switchroles->add($name, $url, self::TYPE_SETTING, null, $key, new pix_icon('i/roles', '')); 03312 } 03313 } 03314 // Return we are done 03315 return $coursenode; 03316 } 03317 03324 protected function add_course_editing_links($course) { 03325 global $CFG; 03326 03327 require_once($CFG->dirroot.'/course/lib.php'); 03328 03329 // Add `add` resources|activities branches 03330 $structurefile = $CFG->dirroot.'/course/format/'.$course->format.'/lib.php'; 03331 if (file_exists($structurefile)) { 03332 require_once($structurefile); 03333 $requestkey = call_user_func('callback_'.$course->format.'_request_key'); 03334 $formatidentifier = optional_param($requestkey, 0, PARAM_INT); 03335 } else { 03336 $requestkey = get_string('section'); 03337 $formatidentifier = optional_param($requestkey, 0, PARAM_INT); 03338 } 03339 03340 $sections = get_all_sections($course->id); 03341 03342 $addresource = $this->add(get_string('addresource')); 03343 $addactivity = $this->add(get_string('addactivity')); 03344 if ($formatidentifier!==0) { 03345 $addresource->force_open(); 03346 $addactivity->force_open(); 03347 } 03348 03349 $this->get_course_modules($course); 03350 03351 $textlib = textlib_get_instance(); 03352 03353 foreach ($sections as $section) { 03354 if ($formatidentifier !== 0 && $section->section != $formatidentifier) { 03355 continue; 03356 } 03357 $sectionurl = new moodle_url('/course/view.php', array('id'=>$course->id, $requestkey=>$section->section)); 03358 if ($section->section == 0) { 03359 $sectionresources = $addresource->add(get_string('course'), $sectionurl, self::TYPE_SETTING); 03360 $sectionactivities = $addactivity->add(get_string('course'), $sectionurl, self::TYPE_SETTING); 03361 } else { 03362 $sectionname = get_section_name($course, $section); 03363 $sectionresources = $addresource->add($sectionname, $sectionurl, self::TYPE_SETTING); 03364 $sectionactivities = $addactivity->add($sectionname, $sectionurl, self::TYPE_SETTING); 03365 } 03366 foreach ($resources as $value=>$resource) { 03367 $url = new moodle_url('/course/mod.php', array('id'=>$course->id, 'sesskey'=>sesskey(), 'section'=>$section->section)); 03368 $pos = strpos($value, '&type='); 03369 if ($pos!==false) { 03370 $url->param('add', $textlib->substr($value, 0,$pos)); 03371 $url->param('type', $textlib->substr($value, $pos+6)); 03372 } else { 03373 $url->param('add', $value); 03374 } 03375 $sectionresources->add($resource, $url, self::TYPE_SETTING); 03376 } 03377 $subbranch = false; 03378 foreach ($activities as $activityname=>$activity) { 03379 if ($activity==='--') { 03380 $subbranch = false; 03381 continue; 03382 } 03383 if (strpos($activity, '--')===0) { 03384 $subbranch = $sectionactivities->add(trim($activity, '-')); 03385 continue; 03386 } 03387 $url = new moodle_url('/course/mod.php', array('id'=>$course->id, 'sesskey'=>sesskey(), 'section'=>$section->section)); 03388 $pos = strpos($activityname, '&type='); 03389 if ($pos!==false) { 03390 $url->param('add', $textlib->substr($activityname, 0,$pos)); 03391 $url->param('type', $textlib->substr($activityname, $pos+6)); 03392 } else { 03393 $url->param('add', $activityname); 03394 } 03395 if ($subbranch !== false) { 03396 $subbranch->add($activity, $url, self::TYPE_SETTING); 03397 } else { 03398 $sectionactivities->add($activity, $url, self::TYPE_SETTING); 03399 } 03400 } 03401 } 03402 } 03403 03415 protected function load_module_settings() { 03416 global $CFG; 03417 03418 if (!$this->page->cm && $this->context->contextlevel == CONTEXT_MODULE && $this->context->instanceid) { 03419 $cm = get_coursemodule_from_id(false, $this->context->instanceid, 0, false, MUST_EXIST); 03420 $this->page->set_cm($cm, $this->page->course); 03421 } 03422 03423 $file = $CFG->dirroot.'/mod/'.$this->page->activityname.'/lib.php'; 03424 if (file_exists($file)) { 03425 require_once($file); 03426 } 03427 03428 $modulenode = $this->add(get_string('pluginadministration', $this->page->activityname)); 03429 $modulenode->force_open(); 03430 03431 // Settings for the module 03432 if (has_capability('moodle/course:manageactivities', $this->page->cm->context)) { 03433 $url = new moodle_url('/course/modedit.php', array('update' => $this->page->cm->id, 'return' => true, 'sesskey' => sesskey())); 03434 $modulenode->add(get_string('editsettings'), $url, navigation_node::TYPE_SETTING, null, 'modedit'); 03435 } 03436 // Assign local roles 03437 if (count(get_assignable_roles($this->page->cm->context))>0) { 03438 $url = new moodle_url('/'.$CFG->admin.'/roles/assign.php', array('contextid'=>$this->page->cm->context->id)); 03439 $modulenode->add(get_string('localroles', 'role'), $url, self::TYPE_SETTING, null, 'roleassign'); 03440 } 03441 // Override roles 03442 if (has_capability('moodle/role:review', $this->page->cm->context) or count(get_overridable_roles($this->page->cm->context))>0) { 03443 $url = new moodle_url('/'.$CFG->admin.'/roles/permissions.php', array('contextid'=>$this->page->cm->context->id)); 03444 $modulenode->add(get_string('permissions', 'role'), $url, self::TYPE_SETTING, null, 'roleoverride'); 03445 } 03446 // Check role permissions 03447 if (has_any_capability(array('moodle/role:assign', 'moodle/role:safeoverride','moodle/role:override', 'moodle/role:assign'), $this->page->cm->context)) { 03448 $url = new moodle_url('/'.$CFG->admin.'/roles/check.php', array('contextid'=>$this->page->cm->context->id)); 03449 $modulenode->add(get_string('checkpermissions', 'role'), $url, self::TYPE_SETTING, null, 'rolecheck'); 03450 } 03451 // Manage filters 03452 if (has_capability('moodle/filter:manage', $this->page->cm->context) && count(filter_get_available_in_context($this->page->cm->context))>0) { 03453 $url = new moodle_url('/filter/manage.php', array('contextid'=>$this->page->cm->context->id)); 03454 $modulenode->add(get_string('filters', 'admin'), $url, self::TYPE_SETTING, null, 'filtermanage'); 03455 } 03456 // Add reports 03457 $reports = get_plugin_list_with_function('report', 'extend_navigation_module', 'lib.php'); 03458 foreach ($reports as $reportfunction) { 03459 $reportfunction($modulenode, $this->page->cm); 03460 } 03461 // Add a backup link 03462 $featuresfunc = $this->page->activityname.'_supports'; 03463 if (function_exists($featuresfunc) && $featuresfunc(FEATURE_BACKUP_MOODLE2) && has_capability('moodle/backup:backupactivity', $this->page->cm->context)) { 03464 $url = new moodle_url('/backup/backup.php', array('id'=>$this->page->cm->course, 'cm'=>$this->page->cm->id)); 03465 $modulenode->add(get_string('backup'), $url, self::TYPE_SETTING, null, 'backup'); 03466 } 03467 03468 // Restore this activity 03469 $featuresfunc = $this->page->activityname.'_supports'; 03470 if (function_exists($featuresfunc) && $featuresfunc(FEATURE_BACKUP_MOODLE2) && has_capability('moodle/restore:restoreactivity', $this->page->cm->context)) { 03471 $url = new moodle_url('/backup/restorefile.php', array('contextid'=>$this->page->cm->context->id)); 03472 $modulenode->add(get_string('restore'), $url, self::TYPE_SETTING, null, 'restore'); 03473 } 03474 03475 // Allow the active advanced grading method plugin to append its settings 03476 $featuresfunc = $this->page->activityname.'_supports'; 03477 if (function_exists($featuresfunc) && $featuresfunc(FEATURE_ADVANCED_GRADING) && has_capability('moodle/grade:managegradingforms', $this->page->cm->context)) { 03478 require_once($CFG->dirroot.'/grade/grading/lib.php'); 03479 $gradingman = get_grading_manager($this->page->cm->context, $this->page->activityname); 03480 $gradingman->extend_settings_navigation($this, $modulenode); 03481 } 03482 03483 $function = $this->page->activityname.'_extend_settings_navigation'; 03484 if (!function_exists($function)) { 03485 return $modulenode; 03486 } 03487 03488 $function($this, $modulenode); 03489 03490 // Remove the module node if there are no children 03491 if (empty($modulenode->children)) { 03492 $modulenode->remove(); 03493 } 03494 03495 return $modulenode; 03496 } 03497 03511 protected function load_user_settings($courseid=SITEID) { 03512 global $USER, $FULLME, $CFG; 03513 03514 if (isguestuser() || !isloggedin()) { 03515 return false; 03516 } 03517 03518 $navusers = $this->page->navigation->get_extending_users(); 03519 03520 if (count($this->userstoextendfor) > 0 || count($navusers) > 0) { 03521 $usernode = null; 03522 foreach ($this->userstoextendfor as $userid) { 03523 if ($userid == $USER->id) { 03524 continue; 03525 } 03526 $node = $this->generate_user_settings($courseid, $userid, 'userviewingsettings'); 03527 if (is_null($usernode)) { 03528 $usernode = $node; 03529 } 03530 } 03531 foreach ($navusers as $user) { 03532 if ($user->id == $USER->id) { 03533 continue; 03534 } 03535 $node = $this->generate_user_settings($courseid, $user->id, 'userviewingsettings'); 03536 if (is_null($usernode)) { 03537 $usernode = $node; 03538 } 03539 } 03540 $this->generate_user_settings($courseid, $USER->id); 03541 } else { 03542 $usernode = $this->generate_user_settings($courseid, $USER->id); 03543 } 03544 return $usernode; 03545 } 03546 03555 public function extend_for_user($userid) { 03556 global $CFG; 03557 03558 if (!in_array($userid, $this->userstoextendfor)) { 03559 $this->userstoextendfor[] = $userid; 03560 if ($this->initialised) { 03561 $this->generate_user_settings($this->page->course->id, $userid, 'userviewingsettings'); 03562 $children = array(); 03563 foreach ($this->children as $child) { 03564 $children[] = $child; 03565 } 03566 array_unshift($children, array_pop($children)); 03567 $this->children = new navigation_node_collection(); 03568 foreach ($children as $child) { 03569 $this->children->add($child); 03570 } 03571 } 03572 } 03573 } 03574 03585 protected function generate_user_settings($courseid, $userid, $gstitle='usercurrentsettings') { 03586 global $DB, $CFG, $USER, $SITE; 03587 03588 if ($courseid != SITEID) { 03589 if (!empty($this->page->course->id) && $this->page->course->id == $courseid) { 03590 $course = $this->page->course; 03591 } else { 03592 list($select, $join) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx'); 03593 $sql = "SELECT c.* $select FROM {course} c $join WHERE c.id = :courseid"; 03594 $course = $DB->get_record_sql($sql, array('courseid' => $courseid), MUST_EXIST); 03595 context_instance_preload($course); 03596 } 03597 } else { 03598 $course = $SITE; 03599 } 03600 03601 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); // Course context 03602 $systemcontext = get_system_context(); 03603 $currentuser = ($USER->id == $userid); 03604 03605 if ($currentuser) { 03606 $user = $USER; 03607 $usercontext = get_context_instance(CONTEXT_USER, $user->id); // User context 03608 } else { 03609 03610 list($select, $join) = context_instance_preload_sql('u.id', CONTEXT_USER, 'ctx'); 03611 $sql = "SELECT u.* $select FROM {user} u $join WHERE u.id = :userid"; 03612 $user = $DB->get_record_sql($sql, array('userid' => $userid), IGNORE_MISSING); 03613 if (!$user) { 03614 return false; 03615 } 03616 context_instance_preload($user); 03617 03618 // Check that the user can view the profile 03619 $usercontext = get_context_instance(CONTEXT_USER, $user->id); // User context 03620 $canviewuser = has_capability('moodle/user:viewdetails', $usercontext); 03621 03622 if ($course->id == SITEID) { 03623 if ($CFG->forceloginforprofiles && !has_coursecontact_role($user->id) && !$canviewuser) { // Reduce possibility of "browsing" userbase at site level 03624 // Teachers can browse and be browsed at site level. If not forceloginforprofiles, allow access (bug #4366) 03625 return false; 03626 } 03627 } else { 03628 $canviewusercourse = has_capability('moodle/user:viewdetails', $coursecontext); 03629 $canaccessallgroups = has_capability('moodle/site:accessallgroups', $coursecontext); 03630 if ((!$canviewusercourse && !$canviewuser) || !can_access_course($course, $user->id)) { 03631 return false; 03632 } 03633 if (!$canaccessallgroups && groups_get_course_groupmode($course) == SEPARATEGROUPS) { 03634 // If groups are in use, make sure we can see that group 03635 return false; 03636 } 03637 } 03638 } 03639 03640 $fullname = fullname($user, has_capability('moodle/site:viewfullnames', $this->page->context)); 03641 03642 $key = $gstitle; 03643 if ($gstitle != 'usercurrentsettings') { 03644 $key .= $userid; 03645 } 03646 03647 // Add a user setting branch 03648 $usersetting = $this->add(get_string($gstitle, 'moodle', $fullname), null, self::TYPE_CONTAINER, null, $key); 03649 $usersetting->id = 'usersettings'; 03650 if ($this->page->context->contextlevel == CONTEXT_USER && $this->page->context->instanceid == $user->id) { 03651 // Automatically start by making it active 03652 $usersetting->make_active(); 03653 } 03654 03655 // Check if the user has been deleted 03656 if ($user->deleted) { 03657 if (!has_capability('moodle/user:update', $coursecontext)) { 03658 // We can't edit the user so just show the user deleted message 03659 $usersetting->add(get_string('userdeleted'), null, self::TYPE_SETTING); 03660 } else { 03661 // We can edit the user so show the user deleted message and link it to the profile 03662 if ($course->id == SITEID) { 03663 $profileurl = new moodle_url('/user/profile.php', array('id'=>$user->id)); 03664 } else { 03665 $profileurl = new moodle_url('/user/view.php', array('id'=>$user->id, 'course'=>$course->id)); 03666 } 03667 $usersetting->add(get_string('userdeleted'), $profileurl, self::TYPE_SETTING); 03668 } 03669 return true; 03670 } 03671 03672 $userauthplugin = false; 03673 if (!empty($user->auth)) { 03674 $userauthplugin = get_auth_plugin($user->auth); 03675 } 03676 03677 // Add the profile edit link 03678 if (isloggedin() && !isguestuser($user) && !is_mnet_remote_user($user)) { 03679 if (($currentuser || is_siteadmin($USER) || !is_siteadmin($user)) && has_capability('moodle/user:update', $systemcontext)) { 03680 $url = new moodle_url('/user/editadvanced.php', array('id'=>$user->id, 'course'=>$course->id)); 03681 $usersetting->add(get_string('editmyprofile'), $url, self::TYPE_SETTING); 03682 } else if ((has_capability('moodle/user:editprofile', $usercontext) && !is_siteadmin($user)) || ($currentuser && has_capability('moodle/user:editownprofile', $systemcontext))) { 03683 if ($userauthplugin && $userauthplugin->can_edit_profile()) { 03684 $url = $userauthplugin->edit_profile_url(); 03685 if (empty($url)) { 03686 $url = new moodle_url('/user/edit.php', array('id'=>$user->id, 'course'=>$course->id)); 03687 } 03688 $usersetting->add(get_string('editmyprofile'), $url, self::TYPE_SETTING); 03689 } 03690 } 03691 } 03692 03693 // Change password link 03694 if ($userauthplugin && $currentuser && !session_is_loggedinas() && !isguestuser() && has_capability('moodle/user:changeownpassword', $systemcontext) && $userauthplugin->can_change_password()) { 03695 $passwordchangeurl = $userauthplugin->change_password_url(); 03696 if (empty($passwordchangeurl)) { 03697 $passwordchangeurl = new moodle_url('/login/change_password.php', array('id'=>$course->id)); 03698 } 03699 $usersetting->add(get_string("changepassword"), $passwordchangeurl, self::TYPE_SETTING); 03700 } 03701 03702 // View the roles settings 03703 if (has_any_capability(array('moodle/role:assign', 'moodle/role:safeoverride','moodle/role:override', 'moodle/role:manage'), $usercontext)) { 03704 $roles = $usersetting->add(get_string('roles'), null, self::TYPE_SETTING); 03705 03706 $url = new moodle_url('/admin/roles/usersroles.php', array('userid'=>$user->id, 'courseid'=>$course->id)); 03707 $roles->add(get_string('thisusersroles', 'role'), $url, self::TYPE_SETTING); 03708 03709 $assignableroles = get_assignable_roles($usercontext, ROLENAME_BOTH); 03710 03711 if (!empty($assignableroles)) { 03712 $url = new moodle_url('/admin/roles/assign.php', array('contextid'=>$usercontext->id,'userid'=>$user->id, 'courseid'=>$course->id)); 03713 $roles->add(get_string('assignrolesrelativetothisuser', 'role'), $url, self::TYPE_SETTING); 03714 } 03715 03716 if (has_capability('moodle/role:review', $usercontext) || count(get_overridable_roles($usercontext, ROLENAME_BOTH))>0) { 03717 $url = new moodle_url('/admin/roles/permissions.php', array('contextid'=>$usercontext->id,'userid'=>$user->id, 'courseid'=>$course->id)); 03718 $roles->add(get_string('permissions', 'role'), $url, self::TYPE_SETTING); 03719 } 03720 03721 $url = new moodle_url('/admin/roles/check.php', array('contextid'=>$usercontext->id,'userid'=>$user->id, 'courseid'=>$course->id)); 03722 $roles->add(get_string('checkpermissions', 'role'), $url, self::TYPE_SETTING); 03723 } 03724 03725 // Portfolio 03726 if ($currentuser && !empty($CFG->enableportfolios) && has_capability('moodle/portfolio:export', $systemcontext)) { 03727 require_once($CFG->libdir . '/portfoliolib.php'); 03728 if (portfolio_instances(true, false)) { 03729 $portfolio = $usersetting->add(get_string('portfolios', 'portfolio'), null, self::TYPE_SETTING); 03730 03731 $url = new moodle_url('/user/portfolio.php', array('courseid'=>$course->id)); 03732 $portfolio->add(get_string('configure', 'portfolio'), $url, self::TYPE_SETTING); 03733 03734 $url = new moodle_url('/user/portfoliologs.php', array('courseid'=>$course->id)); 03735 $portfolio->add(get_string('logs', 'portfolio'), $url, self::TYPE_SETTING); 03736 } 03737 } 03738 03739 $enablemanagetokens = false; 03740 if (!empty($CFG->enablerssfeeds)) { 03741 $enablemanagetokens = true; 03742 } else if (!is_siteadmin($USER->id) 03743 && !empty($CFG->enablewebservices) 03744 && has_capability('moodle/webservice:createtoken', get_system_context()) ) { 03745 $enablemanagetokens = true; 03746 } 03747 // Security keys 03748 if ($currentuser && $enablemanagetokens) { 03749 $url = new moodle_url('/user/managetoken.php', array('sesskey'=>sesskey())); 03750 $usersetting->add(get_string('securitykeys', 'webservice'), $url, self::TYPE_SETTING); 03751 } 03752 03753 // Repository 03754 if (!$currentuser && $usercontext->contextlevel == CONTEXT_USER) { 03755 if (!$this->cache->cached('contexthasrepos'.$usercontext->id)) { 03756 require_once($CFG->dirroot . '/repository/lib.php'); 03757 $editabletypes = repository::get_editable_types($usercontext); 03758 $haseditabletypes = !empty($editabletypes); 03759 unset($editabletypes); 03760 $this->cache->set('contexthasrepos'.$usercontext->id, $haseditabletypes); 03761 } else { 03762 $haseditabletypes = $this->cache->{'contexthasrepos'.$usercontext->id}; 03763 } 03764 if ($haseditabletypes) { 03765 $url = new moodle_url('/repository/manage_instances.php', array('contextid'=>$usercontext->id)); 03766 $usersetting->add(get_string('repositories', 'repository'), $url, self::TYPE_SETTING); 03767 } 03768 } 03769 03770 // Messaging 03771 if (($currentuser && has_capability('moodle/user:editownmessageprofile', $systemcontext)) || (!isguestuser($user) && has_capability('moodle/user:editmessageprofile', $usercontext) && !is_primary_admin($user->id))) { 03772 $url = new moodle_url('/message/edit.php', array('id'=>$user->id, 'course'=>$course->id)); 03773 $usersetting->add(get_string('editmymessage', 'message'), $url, self::TYPE_SETTING); 03774 } 03775 03776 // Blogs 03777 if ($currentuser && !empty($CFG->bloglevel)) { 03778 $blog = $usersetting->add(get_string('blogs', 'blog'), null, navigation_node::TYPE_CONTAINER, null, 'blogs'); 03779 $blog->add(get_string('preferences', 'blog'), new moodle_url('/blog/preferences.php'), navigation_node::TYPE_SETTING); 03780 if (!empty($CFG->useexternalblogs) && $CFG->maxexternalblogsperuser > 0 && has_capability('moodle/blog:manageexternal', get_context_instance(CONTEXT_SYSTEM))) { 03781 $blog->add(get_string('externalblogs', 'blog'), new moodle_url('/blog/external_blogs.php'), navigation_node::TYPE_SETTING); 03782 $blog->add(get_string('addnewexternalblog', 'blog'), new moodle_url('/blog/external_blog_edit.php'), navigation_node::TYPE_SETTING); 03783 } 03784 } 03785 03786 // Login as ... 03787 if (!$user->deleted and !$currentuser && !session_is_loggedinas() && has_capability('moodle/user:loginas', $coursecontext) && !is_siteadmin($user->id)) { 03788 $url = new moodle_url('/course/loginas.php', array('id'=>$course->id, 'user'=>$user->id, 'sesskey'=>sesskey())); 03789 $usersetting->add(get_string('loginas'), $url, self::TYPE_SETTING); 03790 } 03791 03792 return $usersetting; 03793 } 03794 03800 protected function load_block_settings() { 03801 global $CFG; 03802 03803 $blocknode = $this->add(print_context_name($this->context)); 03804 $blocknode->force_open(); 03805 03806 // Assign local roles 03807 $assignurl = new moodle_url('/'.$CFG->admin.'/roles/assign.php', array('contextid'=>$this->context->id)); 03808 $blocknode->add(get_string('assignroles', 'role'), $assignurl, self::TYPE_SETTING); 03809 03810 // Override roles 03811 if (has_capability('moodle/role:review', $this->context) or count(get_overridable_roles($this->context))>0) { 03812 $url = new moodle_url('/'.$CFG->admin.'/roles/permissions.php', array('contextid'=>$this->context->id)); 03813 $blocknode->add(get_string('permissions', 'role'), $url, self::TYPE_SETTING); 03814 } 03815 // Check role permissions 03816 if (has_any_capability(array('moodle/role:assign', 'moodle/role:safeoverride','moodle/role:override', 'moodle/role:assign'), $this->context)) { 03817 $url = new moodle_url('/'.$CFG->admin.'/roles/check.php', array('contextid'=>$this->context->id)); 03818 $blocknode->add(get_string('checkpermissions', 'role'), $url, self::TYPE_SETTING); 03819 } 03820 03821 return $blocknode; 03822 } 03823 03829 protected function load_category_settings() { 03830 global $CFG; 03831 03832 $categorynode = $this->add(print_context_name($this->context)); 03833 $categorynode->force_open(); 03834 03835 if (has_any_capability(array('moodle/category:manage', 'moodle/course:create'), $this->context)) { 03836 $url = new moodle_url('/course/category.php', array('id'=>$this->context->instanceid, 'sesskey'=>sesskey())); 03837 if ($this->page->user_is_editing()) { 03838 $url->param('categoryedit', '0'); 03839 $editstring = get_string('turneditingoff'); 03840 } else { 03841 $url->param('categoryedit', '1'); 03842 $editstring = get_string('turneditingon'); 03843 } 03844 $categorynode->add($editstring, $url, self::TYPE_SETTING, null, null, new pix_icon('i/edit', '')); 03845 } 03846 03847 if ($this->page->user_is_editing() && has_capability('moodle/category:manage', $this->context)) { 03848 $editurl = new moodle_url('/course/editcategory.php', array('id' => $this->context->instanceid)); 03849 $categorynode->add(get_string('editcategorythis'), $editurl, self::TYPE_SETTING, null, 'edit', new pix_icon('i/edit', '')); 03850 03851 $addsubcaturl = new moodle_url('/course/editcategory.php', array('parent' => $this->context->instanceid)); 03852 $categorynode->add(get_string('addsubcategory'), $addsubcaturl, self::TYPE_SETTING, null, 'addsubcat', new pix_icon('i/withsubcat', '')); 03853 } 03854 03855 // Assign local roles 03856 if (has_capability('moodle/role:assign', $this->context)) { 03857 $assignurl = new moodle_url('/'.$CFG->admin.'/roles/assign.php', array('contextid'=>$this->context->id)); 03858 $categorynode->add(get_string('assignroles', 'role'), $assignurl, self::TYPE_SETTING, null, 'roles', new pix_icon('i/roles', '')); 03859 } 03860 03861 // Override roles 03862 if (has_capability('moodle/role:review', $this->context) or count(get_overridable_roles($this->context))>0) { 03863 $url = new moodle_url('/'.$CFG->admin.'/roles/permissions.php', array('contextid'=>$this->context->id)); 03864 $categorynode->add(get_string('permissions', 'role'), $url, self::TYPE_SETTING, null, 'permissions', new pix_icon('i/permissions', '')); 03865 } 03866 // Check role permissions 03867 if (has_any_capability(array('moodle/role:assign', 'moodle/role:safeoverride','moodle/role:override', 'moodle/role:assign'), $this->context)) { 03868 $url = new moodle_url('/'.$CFG->admin.'/roles/check.php', array('contextid'=>$this->context->id)); 03869 $categorynode->add(get_string('checkpermissions', 'role'), $url, self::TYPE_SETTING, null, 'checkpermissions', new pix_icon('i/checkpermissions', '')); 03870 } 03871 03872 // Cohorts 03873 if (has_capability('moodle/cohort:manage', $this->context) or has_capability('moodle/cohort:view', $this->context)) { 03874 $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', '')); 03875 } 03876 03877 // Manage filters 03878 if (has_capability('moodle/filter:manage', $this->context) && count(filter_get_available_in_context($this->context))>0) { 03879 $url = new moodle_url('/filter/manage.php', array('contextid'=>$this->context->id)); 03880 $categorynode->add(get_string('filters', 'admin'), $url, self::TYPE_SETTING, null, 'filters', new pix_icon('i/filter', '')); 03881 } 03882 03883 return $categorynode; 03884 } 03885 03896 protected function in_alternative_role() { 03897 global $USER; 03898 if (!empty($USER->access['rsw']) && is_array($USER->access['rsw'])) { 03899 if (!empty($this->page->context) && !empty($USER->access['rsw'][$this->page->context->path])) { 03900 return $USER->access['rsw'][$this->page->context->path]; 03901 } 03902 foreach ($USER->access['rsw'] as $key=>$role) { 03903 if (strpos($this->context->path,$key)===0) { 03904 return $role; 03905 } 03906 } 03907 } 03908 return false; 03909 } 03910 03916 protected function load_front_page_settings($forceopen = false) { 03917 global $SITE, $CFG; 03918 03919 $course = clone($SITE); 03920 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); // Course context 03921 03922 $frontpage = $this->add(get_string('frontpagesettings'), null, self::TYPE_SETTING, null, 'frontpage'); 03923 if ($forceopen) { 03924 $frontpage->force_open(); 03925 } 03926 $frontpage->id = 'frontpagesettings'; 03927 03928 if (has_capability('moodle/course:update', $coursecontext)) { 03929 03930 // Add the turn on/off settings 03931 $url = new moodle_url('/course/view.php', array('id'=>$course->id, 'sesskey'=>sesskey())); 03932 if ($this->page->user_is_editing()) { 03933 $url->param('edit', 'off'); 03934 $editstring = get_string('turneditingoff'); 03935 } else { 03936 $url->param('edit', 'on'); 03937 $editstring = get_string('turneditingon'); 03938 } 03939 $frontpage->add($editstring, $url, self::TYPE_SETTING, null, null, new pix_icon('i/edit', '')); 03940 03941 // Add the course settings link 03942 $url = new moodle_url('/admin/settings.php', array('section'=>'frontpagesettings')); 03943 $frontpage->add(get_string('editsettings'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/settings', '')); 03944 } 03945 03946 // add enrol nodes 03947 enrol_add_course_navigation($frontpage, $course); 03948 03949 // Manage filters 03950 if (has_capability('moodle/filter:manage', $coursecontext) && count(filter_get_available_in_context($coursecontext))>0) { 03951 $url = new moodle_url('/filter/manage.php', array('contextid'=>$coursecontext->id)); 03952 $frontpage->add(get_string('filters', 'admin'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/filter', '')); 03953 } 03954 03955 // Backup this course 03956 if (has_capability('moodle/backup:backupcourse', $coursecontext)) { 03957 $url = new moodle_url('/backup/backup.php', array('id'=>$course->id)); 03958 $frontpage->add(get_string('backup'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/backup', '')); 03959 } 03960 03961 // Restore to this course 03962 if (has_capability('moodle/restore:restorecourse', $coursecontext)) { 03963 $url = new moodle_url('/backup/restorefile.php', array('contextid'=>$coursecontext->id)); 03964 $frontpage->add(get_string('restore'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/restore', '')); 03965 } 03966 03967 // Questions 03968 require_once($CFG->libdir . '/questionlib.php'); 03969 question_extend_settings_navigation($frontpage, $coursecontext)->trim_if_empty(); 03970 03971 // Manage files 03972 if ($course->legacyfiles == 2 and has_capability('moodle/course:managefiles', $this->context)) { 03973 //hiden in new installs 03974 $url = new moodle_url('/files/index.php', array('contextid'=>$coursecontext->id, 'itemid'=>0, 'component' => 'course', 'filearea'=>'legacy')); 03975 $frontpage->add(get_string('sitelegacyfiles'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/files', '')); 03976 } 03977 return $frontpage; 03978 } 03979 03983 public function clear_cache() { 03984 $this->cache->volatile(); 03985 } 03986 } 03987 03996 class navigation_json { 03998 protected $nodetype = array('node','branch'); 04000 protected $expandable = array(); 04007 public function convert($branch) { 04008 $xml = $this->convert_child($branch); 04009 return $xml; 04010 } 04016 public function set_expandable($expandable) { 04017 foreach ($expandable as $node) { 04018 $this->expandable[$node['key'].':'.$node['type']] = $node; 04019 } 04020 } 04028 protected function convert_child($child, $depth=1) { 04029 if (!$child->display) { 04030 return ''; 04031 } 04032 $attributes = array(); 04033 $attributes['id'] = $child->id; 04034 $attributes['name'] = $child->text; 04035 $attributes['type'] = $child->type; 04036 $attributes['key'] = $child->key; 04037 $attributes['class'] = $child->get_css_type(); 04038 04039 if ($child->icon instanceof pix_icon) { 04040 $attributes['icon'] = array( 04041 'component' => $child->icon->component, 04042 'pix' => $child->icon->pix, 04043 ); 04044 foreach ($child->icon->attributes as $key=>$value) { 04045 if ($key == 'class') { 04046 $attributes['icon']['classes'] = explode(' ', $value); 04047 } else if (!array_key_exists($key, $attributes['icon'])) { 04048 $attributes['icon'][$key] = $value; 04049 } 04050 04051 } 04052 } else if (!empty($child->icon)) { 04053 $attributes['icon'] = (string)$child->icon; 04054 } 04055 04056 if ($child->forcetitle || $child->title !== $child->text) { 04057 $attributes['title'] = htmlentities($child->title); 04058 } 04059 if (array_key_exists($child->key.':'.$child->type, $this->expandable)) { 04060 $attributes['expandable'] = $child->key; 04061 $child->add_class($this->expandable[$child->key.':'.$child->type]['id']); 04062 } 04063 04064 if (count($child->classes)>0) { 04065 $attributes['class'] .= ' '.join(' ',$child->classes); 04066 } 04067 if (is_string($child->action)) { 04068 $attributes['link'] = $child->action; 04069 } else if ($child->action instanceof moodle_url) { 04070 $attributes['link'] = $child->action->out(); 04071 } else if ($child->action instanceof action_link) { 04072 $attributes['link'] = $child->action->url->out(); 04073 } 04074 $attributes['hidden'] = ($child->hidden); 04075 $attributes['haschildren'] = ($child->children->count()>0 || $child->type == navigation_node::TYPE_CATEGORY); 04076 04077 if ($child->children->count() > 0) { 04078 $attributes['children'] = array(); 04079 foreach ($child->children as $subchild) { 04080 $attributes['children'][] = $this->convert_child($subchild, $depth+1); 04081 } 04082 } 04083 04084 if ($depth > 1) { 04085 return $attributes; 04086 } else { 04087 return json_encode($attributes); 04088 } 04089 } 04090 } 04091 04113 class navigation_cache { 04115 protected $creation; 04117 protected $session; 04119 protected $area; 04121 protected $timeout; 04123 protected $currentcontext; 04125 const CACHETIME = 0; 04127 const CACHEUSERID = 1; 04129 const CACHEVALUE = 2; 04131 public static $volatilecaches; 04132 04142 public function __construct($area, $timeout=1800) { 04143 $this->creation = time(); 04144 $this->area = $area; 04145 $this->timeout = time() - $timeout; 04146 if (rand(0,100) === 0) { 04147 $this->garbage_collection(); 04148 } 04149 } 04150 04157 protected function ensure_session_cache_initialised() { 04158 global $SESSION; 04159 if (empty($this->session)) { 04160 if (!isset($SESSION->navcache)) { 04161 $SESSION->navcache = new stdClass; 04162 } 04163 if (!isset($SESSION->navcache->{$this->area})) { 04164 $SESSION->navcache->{$this->area} = array(); 04165 } 04166 $this->session = &$SESSION->navcache->{$this->area}; 04167 } 04168 } 04169 04176 public function __get($key) { 04177 if (!$this->cached($key)) { 04178 return; 04179 } 04180 $information = $this->session[$key][self::CACHEVALUE]; 04181 return unserialize($information); 04182 } 04183 04190 public function __set($key, $information) { 04191 $this->set($key, $information); 04192 } 04193 04200 public function set($key, $information) { 04201 global $USER; 04202 $this->ensure_session_cache_initialised(); 04203 $information = serialize($information); 04204 $this->session[$key]= array(self::CACHETIME=>time(), self::CACHEUSERID=>$USER->id, self::CACHEVALUE=>$information); 04205 } 04212 public function cached($key) { 04213 global $USER; 04214 $this->ensure_session_cache_initialised(); 04215 if (!array_key_exists($key, $this->session) || !is_array($this->session[$key]) || $this->session[$key][self::CACHEUSERID]!=$USER->id || $this->session[$key][self::CACHETIME] < $this->timeout) { 04216 return false; 04217 } 04218 return true; 04219 } 04230 public function compare($key, $value, $serialise = true) { 04231 if ($this->cached($key)) { 04232 if ($serialise) { 04233 $value = serialize($value); 04234 } 04235 if ($this->session[$key][self::CACHEVALUE] === $value) { 04236 return true; 04237 } 04238 } 04239 return false; 04240 } 04244 public function clear() { 04245 global $SESSION; 04246 unset($SESSION->navcache); 04247 $this->session = null; 04248 } 04252 protected function garbage_collection() { 04253 if (empty($this->session)) { 04254 return true; 04255 } 04256 foreach ($this->session as $key=>$cachedinfo) { 04257 if (is_array($cachedinfo) && $cachedinfo[self::CACHETIME]<$this->timeout) { 04258 unset($this->session[$key]); 04259 } 04260 } 04261 } 04262 04272 public function volatile($setting = true) { 04273 if (self::$volatilecaches===null) { 04274 self::$volatilecaches = array(); 04275 register_shutdown_function(array('navigation_cache','destroy_volatile_caches')); 04276 } 04277 04278 if ($setting) { 04279 self::$volatilecaches[$this->area] = $this->area; 04280 } else if (array_key_exists($this->area, self::$volatilecaches)) { 04281 unset(self::$volatilecaches[$this->area]); 04282 } 04283 } 04284 04293 public static function destroy_volatile_caches() { 04294 global $SESSION; 04295 if (is_array(self::$volatilecaches) && count(self::$volatilecaches)>0) { 04296 foreach (self::$volatilecaches as $area) { 04297 $SESSION->navcache->{$area} = array(); 04298 } 04299 } else { 04300 $SESSION->navcache = new stdClass; 04301 } 04302 } 04303 }