navigation MDL-14632 Very significant navigation commit
[moodle.git] / blocks / global_navigation_tree / block_global_navigation_tree.php
CommitLineData
7d2a0492 1<?php
2
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17
18/**
19 * This file contains classes used to manage the navigation structures in Moodle
20 * and was introduced as part of the changes occuring in Moodle 2.0
21 *
22 * @since 2.0
23 * @package blocks
24 * @copyright 2009 Sam Hemelryk
25 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 */
27
28/**
29 * The global navigation tree block class
30 *
31 * Used to produce the global navigation block new to Moodle 2.0
32 *
33 * @package blocks
34 * @copyright 2009 Sam Hemelryk
35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36 */
37class block_global_navigation_tree extends block_tree {
38
39 /** @var int */
40 public static $navcount;
41 /** @var string */
42 public $blockname = null;
43 protected $contentgenerated = false;
44
45 /**
46 * Set the initial properties for the block
47 */
48 function init() {
49 global $CFG;
50 $this->blockname = get_class($this);
51 $this->title = get_string('blockname', $this->blockname);
52 $this->version = 2009082800;
53 }
54
55 /**
56 * All multiple instances of this block
57 * @return bool Returns true
58 */
59 function instance_allow_multiple() {
60 return false;
61 }
62
63 /**
64 * Set the applicable formats for this block to all
65 * @return array
66 */
67 function applicable_formats() {
68 return array('all' => true);
69 }
70
71 /**
72 * Allow the user to configure a block instance
73 * @return bool Returns true
74 */
75 function instance_allow_config() {
76 return true;
77 }
78
79 /**
80 * Gets the content for this block by grabbing it from $this->page
81 */
82 function get_content() {
83 global $CFG, $OUTPUT;
84 // First check if we have already generated, don't waste cycles
85 if ($this->contentgenerated === true) {
86 return true;
87 }
88 $this->page->requires->js('lib/javascript-navigation.js');
89 // Navcount is used to allow us to have multiple trees although I dont' know why
90 // you would want to trees the same
91
92 block_global_navigation_tree::$navcount++;
93
94 // Set the expansionlimit if one has been set in block config
95 if (!empty($this->config->expansionlimit) && $this->config->expansionlimit!='0') {
96 $this->page->navigation->expansionlimit = $this->config->expansionlimit;
97 }
98
99 // Initialise (only actually happens if it hasn't already been done yet
100 $this->page->navigation->initialise();
101
102 // Remove empty branches if the user has selected to
103
104 if (empty($this->config->showemptybranches) || $this->config->showemptybranches=='no') {
105 $this->remove_empty_section_branches();
106 }
107
108 // Load the my courses branch if the user has selected to
109 if (!empty($this->config->showmycourses) && $this->config->showmycourses=='yes') {
110 $this->showmycourses();
111 }
112
113 if (!empty($this->config->showmyhistory) && $this->config->showmyhistory=='yes') {
114 $this->showmyhistory();
115 }
116
117 $tooglesidetabdisplay = get_string('tooglesidetabdisplay', $this->blockname);
118 $toogleblockdisplay = get_string('toogleblockdisplay', $this->blockname);
119
120
121 // Get the expandable items so we can pass them to JS
122 $expandable = array();
123 $this->page->navigation->find_expandable($expandable);
124 $args = array('expansions'=>$expandable,'instance'=>$this->instance->id);
125 $args['tooglesidetabdisplay'] = $tooglesidetabdisplay;
126 $args['toogleblockdisplay'] = $toogleblockdisplay;
127 // Give JS some information we will use within the JS tree object
128 $this->page->requires->data_for_js('globalnav'.block_global_navigation_tree::$navcount, $args);
129 // Initialise the JS tree object
130 $this->id = 'globalnav'.block_global_navigation_tree::$navcount;
131 $this->page->requires->js_function_call('setup_new_navtree', array($this->id))->on_dom_ready();
132 // Grab the items to display
133 $this->content->items = array($this->page->navigation);
134
135 $url = $this->page->url;
136 $url->param('regenerate','navigation');
137 $reloadstr = get_string('reload');
138 $this->content->footer .= '<a href="'.$url->out().'" class="customcommand"><img src="'.$OUTPUT->old_icon_url('t/reload').'" alt="'.$reloadstr.'" title="'.$reloadstr.'" /></a>';
139 if (empty($this->config->enablesidebarpopout) || $this->config->enablesidebarpopout == 'yes') {
140 user_preference_allow_ajax_update('nav_in_tab_panel_globalnav'.block_global_navigation_tree::$navcount, PARAM_INT);
141 if (get_user_preferences('nav_in_tab_panel_globalnav'.block_global_navigation_tree::$navcount, 0)) {
142 $icon = $OUTPUT->old_icon_url('t/movetoblock');
143 $string = $toogleblockdisplay;
144 } else {
145 $icon = $OUTPUT->old_icon_url('t/movetosidetab');
146 $string = $tooglesidetabdisplay;
147 }
148 $this->content->footer .= '<a class="moveto customcommand requiresjs"><img src="'.$icon.'" alt="'.$string.'" title="'.$string.'"></a>';
149 }
150
151 // Set content generated to true so that we know it has been done
152 $this->contentgenerated = true;
153 return true;
154 }
155
156 /**
157 * Returns the attributes to set for this block
158 *
159 * This function returns an array of HTML attributes for this block including
160 * the defaults
161 * {@link block_tree->html_attributes()} is used to get the default arguments
162 * and then we check whether the user has enabled hover expansion and add the
163 * appropriate hover class if it has
164 *
165 * @return array An array of HTML attributes
166 */
167 public function html_attributes() {
168 $attributes = parent::html_attributes();
169 if (!empty($this->config->enablehoverexpansion) && $this->config->enablehoverexpansion == 'yes') {
170 $attributes['class'] .= ' sideblock_js_expansion';
171 }
172 if (get_user_preferences('nav_in_tab_panel_globalnav'.block_global_navigation_tree::$navcount, 0)) {
173 $attributes['class'] .= ' sideblock_js_sidebarpopout';
174 }
175 return $attributes;
176 }
177
178 /**
179 * This function maintains a history of the active pages that a user has visited
180 * and displays it back to the user as part of the navigation structure
181 *
182 * @return bool
183 */
184 protected function showmyhistory() {
185 global $USER, $PAGE, $ME;
186
187 // Create a navigation cache so that we can store the history
188 $cache = new navigation_cache('navigationhistory', 60*60);
189
190 // If the user isn't logged in or is a guest we don't want to display anything
191 if (!isloggedin() || isguest()) {
192 return false;
193 }
194
195 // Check the cache to see if we have loaded my courses already
196 // there is a very good chance that we have
197 if (!$cache->cached('history')) {
198 $cache->history = array();
199 }
200 $history = $cache->history;
201 $historycount = count($history);
202
203 // Find the initial active node
204 $child = false;
205 if ($PAGE->navigation->contains_active_node()) {
206 $child = $PAGE->navigation->find_active_node();
207 } else if ($PAGE->settingsnav->contains_active_node()) {
208 $child = $PAGE->settingsnav->find_active_node();
209 }
210 // Check that we found an active child node
211 if ($child!==false) {
212 $properties = array();
213 // Check whether this child contains another active child node
214 // this can happen if we are looking at a module
215 if ($child->contains_active_node()) {
216 $titlebits = array();
217 // Loop while the child contains active nodes and in each iteration
218 // find the next node in the correct direction
219 while ($child!==null && $child->contains_active_node()) {
220 if (!empty($child->shorttext)) {
221 $titlebits[] = $child->shorttext;
222 } else {
223 $titlebits[] = $child->text;
224 }
225 foreach ($child->children as $child) {
226 if ($child->contains_active_node() || $child->isactive) {
227 // We have found the active child or one of its parents
228 // so break the foreach so we can proceed in the while
229 break;
230 }
231 }
232 }
233 if (!empty($child->shorttext)) {
234 $titlebits[] = $child->shorttext;
235 } else {
236 $titlebits[] = $child->text;
237 }
238 $properties['text'] = join(' - ', $titlebits);
239 $properties['shorttext'] = join(' - ', $titlebits);
240 } else {
241 $properties['text'] = $child->text;
242 $properties['shorttext'] = $child->shorttext;
243 }
244 $properties['action'] = $child->action;
245 $properties['key'] = $child->key;
246 $properties['type'] = $child->type;
247 $properties['icon'] = $child->icon;
248
249 // Create a new navigation node object free of the main structure
250 // so that it is easily storeable and customised
251 $child = new navigation_node($properties);
252
253 // Check that this page isn't already in the history array. If it is
254 // we will remove it so that it gets added at the top and we dont get
255 // duplicate entries
256 foreach ($history as $key=>$node) {
257 if ($node->key == $child->key && $node->type == $child->type) {
258 if ($node->action instanceof moodle_url && $child->action instanceof moodle_url && $node->action->compare($child->action)) {
259 unset($history[$key]);
260 } else if ($child->action instanceof moodle_url && $child->action->out(true) == $node->action) {
261 unset($history[$key]);
262 } else if ($child->action == $node->action) {
263 unset($history[$key]);
264 }
265 }
266 }
267 // If there is more than 5 elements in the array remove the first one
268 // We want a fifo array
269 if (count($history) > 5) {
270 array_shift($history);
271 }
272 $child->nodetype = navigation_node::NODETYPE_LEAF;
273 $child->children = array();
274 // Add the child to the history array
275 array_push($history,$child);
276 }
277
278 // If we have `more than nothing` in the history display it :D
279 if ($historycount > 0) {
280 // Add a branch to hold the users history
281 $myhistorybranch = $PAGE->navigation->add(get_string('showmyhistorytitle', $this->blockname), null, 'myhistory',navigation_node::TYPE_CATEGORY);
282 $PAGE->navigation->get($myhistorybranch)->children = array_reverse($history);
283 }
284
285 // Cache the history (or update the cached history as it is)
286 $cache->history = $history;
287
288 return true;
289 }
290
291 /**
292 * This function loads the users my courses array into the navigation
293 *
294 * @return bool
295 */
296 protected function showmycourses() {
297 global $USER, $PAGE;
298
299 // Create a navigation cache to point at the same node as the main navigation
300 // cache
301 $cache = new navigation_cache('navigation');
302
303 // If the user isn't logged in or is a guest we don't want to display anything
304 if (!isloggedin() || isguest()) {
305 return false;
306 }
307
308 // Check the cache to see if we have loaded my courses already
309 // there is a very good chance that we have
310 if (!$cache->cached('mycourses')) {
311 $cache->mycourses = get_my_courses($USER->id);
312 }
313 $courses = $cache->mycourses;
314
315 // If no courses to display here, return before adding anything
316 if (!is_array($courses) || count($courses)==0) {
317 return false;
318 }
319
320 // Add a branch labelled something like My Courses
321 $mycoursesbranch = $PAGE->navigation->add(get_string('mycourses'), null, 'mycourses',navigation_node::TYPE_CATEGORY);
322 $PAGE->navigation->add_courses($courses, $mycoursesbranch);
323
324 return true;
325 }
326
327 /**
328 * This function searches all branches and removes any empty section branches
329 * for the global navigation structure. This is usually called by the global
330 * navigation block based on a block setting
331 */
332 protected function remove_empty_section_branches() {
333 global $PAGE;
334 $cache = new navigation_cache('navigation');
335 $course = &$PAGE->navigation->find_active_node(navigation_node::TYPE_COURSE);
336 if ($course===false || !$cache->cached('modinfo'.$course->key) || !$cache->cached('coursesections'.$course->key)) {
337 return 0;
338 }
339 $sectionstoremove = array();
340 $coursesections = $cache->{'coursesections'.$course->key};
341 $modinfosections = $cache->{'modinfo'.$course->key}->sections;
342 foreach ($coursesections as $id=>$section) {
343 if (!array_key_exists($id, $modinfosections)) {
344 $sectionstoremove[] = $section->id;
345 }
346 }
347
348 foreach ($course->children as $key=>$node) {
349 if ($node->type == navigation_node::TYPE_SECTION && in_array($node->key, $sectionstoremove)) {
350 $course->remove_child($key);
351 }
352 }
353 }
354}