Commit | Line | Data |
---|---|---|
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 | */ | |
37 | class block_global_navigation_tree extends block_tree { | |
38 | ||
39 | /** @var int */ | |
40 | public static $navcount; | |
41 | /** @var string */ | |
42 | public $blockname = null; | |
d7319652 | 43 | /** @var bool */ |
7d2a0492 | 44 | protected $contentgenerated = false; |
d7319652 | 45 | /** @var bool|null */ |
46 | protected $docked = null; | |
7d2a0492 | 47 | |
48 | /** | |
49 | * Set the initial properties for the block | |
50 | */ | |
51 | function init() { | |
52 | global $CFG; | |
53 | $this->blockname = get_class($this); | |
621f2f28 | 54 | $this->title = get_string('pluginname', $this->blockname); |
7d2a0492 | 55 | $this->version = 2009082800; |
56 | } | |
57 | ||
58 | /** | |
59 | * All multiple instances of this block | |
60 | * @return bool Returns true | |
61 | */ | |
62 | function instance_allow_multiple() { | |
63 | return false; | |
64 | } | |
65 | ||
66 | /** | |
67 | * Set the applicable formats for this block to all | |
68 | * @return array | |
69 | */ | |
70 | function applicable_formats() { | |
71 | return array('all' => true); | |
72 | } | |
73 | ||
74 | /** | |
75 | * Allow the user to configure a block instance | |
76 | * @return bool Returns true | |
77 | */ | |
78 | function instance_allow_config() { | |
79 | return true; | |
80 | } | |
81 | ||
9d897331 | 82 | function get_required_javascript() { |
1ce15fda | 83 | global $CFG; |
9d897331 | 84 | $this->_initialise_dock(); |
7c964cdf | 85 | $this->page->requires->js_module(array('name'=>'core_dock', 'fullpath'=>'/blocks/dock.js', 'requires'=>array('base', 'cookie', 'dom', 'io', 'node', 'event-custom', 'event-mouseenter', 'yui2-container'))); |
507a7a9a | 86 | $this->page->requires->js_module(array('name'=>'block_navigation', 'fullpath'=>'/blocks/global_navigation_tree/navigation.js', 'requires'=>array('core_dock', 'io', 'node', 'dom', 'event-custom', 'json-parse'))); |
9d897331 SH |
87 | user_preference_allow_ajax_update('docked_block_instance_'.$this->instance->id, PARAM_INT); |
88 | } | |
89 | ||
7d2a0492 | 90 | /** |
91 | * Gets the content for this block by grabbing it from $this->page | |
92 | */ | |
93 | function get_content() { | |
94 | global $CFG, $OUTPUT; | |
95 | // First check if we have already generated, don't waste cycles | |
96 | if ($this->contentgenerated === true) { | |
4f0c2d00 | 97 | return $this->content; |
7d2a0492 | 98 | } |
f44b10ed | 99 | $this->page->requires->yui2_lib('dom'); |
2574fae6 | 100 | // JS for navigation moved to the standard theme, the code will probably have to depend on the actual page structure |
9dec75db | 101 | // $this->page->requires->js('/lib/javascript-navigation.js'); |
7d2a0492 | 102 | // Navcount is used to allow us to have multiple trees although I dont' know why |
103 | // you would want to trees the same | |
c1c0cecf | 104 | |
7d2a0492 | 105 | block_global_navigation_tree::$navcount++; |
c1c0cecf | 106 | |
d7319652 | 107 | // Check if this block has been docked |
108 | if ($this->docked === null) { | |
109 | $this->docked = get_user_preferences('nav_in_tab_panel_globalnav'.block_global_navigation_tree::$navcount, 0); | |
110 | } | |
111 | ||
112 | // Check if there is a param to change the docked state | |
113 | if ($this->docked && optional_param('undock', null, PARAM_INT)==$this->instance->id) { | |
114 | unset_user_preference('nav_in_tab_panel_globalnav'.block_global_navigation_tree::$navcount); | |
115 | $url = $this->page->url; | |
116 | $url->remove_params(array('undock')); | |
117 | redirect($url); | |
118 | } else if (!$this->docked && optional_param('dock', null, PARAM_INT)==$this->instance->id) { | |
119 | set_user_preferences(array('nav_in_tab_panel_globalnav'.block_global_navigation_tree::$navcount=>1)); | |
120 | $url = $this->page->url; | |
121 | $url->remove_params(array('dock')); | |
122 | redirect($url); | |
123 | } | |
124 | ||
7d2a0492 | 125 | // Set the expansionlimit if one has been set in block config |
126 | if (!empty($this->config->expansionlimit) && $this->config->expansionlimit!='0') { | |
127 | $this->page->navigation->expansionlimit = $this->config->expansionlimit; | |
128 | } | |
129 | ||
130 | // Initialise (only actually happens if it hasn't already been done yet | |
131 | $this->page->navigation->initialise(); | |
132 | ||
133 | // Remove empty branches if the user has selected to | |
c1c0cecf | 134 | |
7d2a0492 | 135 | if (empty($this->config->showemptybranches) || $this->config->showemptybranches=='no') { |
136 | $this->remove_empty_section_branches(); | |
137 | } | |
138 | ||
da3ab9c4 | 139 | // Load the my courses branch if the user has selected to |
8958174c | 140 | if (isset($CFG->navshowcategories) && empty($CFG->navshowcategories)) { |
da3ab9c4 SH |
141 | $this->page->navigation->collapse_course_categories(); |
142 | } | |
143 | ||
7d2a0492 | 144 | // Load the my courses branch if the user has selected to |
145 | if (!empty($this->config->showmycourses) && $this->config->showmycourses=='yes') { | |
146 | $this->showmycourses(); | |
147 | } | |
148 | ||
149 | if (!empty($this->config->showmyhistory) && $this->config->showmyhistory=='yes') { | |
150 | $this->showmyhistory(); | |
151 | } | |
152 | ||
7d2a0492 | 153 | // Get the expandable items so we can pass them to JS |
154 | $expandable = array(); | |
155 | $this->page->navigation->find_expandable($expandable); | |
d2c394f3 | 156 | |
7d2a0492 | 157 | // Initialise the JS tree object |
507a7a9a | 158 | $module = array('name'=>'block_navigation', 'fullpath'=>'/blocks/global_navigation_tree/navigation.js', 'requires'=>array('core_dock', 'io', 'node', 'dom', 'event-custom', 'json-parse')); |
781bd8ae PS |
159 | $arguments = array($this->instance->id, array('expansions'=>$expandable, 'instance'=>$this->instance->id, 'candock'=>$this->instance_can_be_docked())); |
160 | $this->page->requires->js_init_call('M.block_navigation.init_add_tree', $arguments, false, $module); | |
4f0c2d00 | 161 | |
7d2a0492 | 162 | // Grab the items to display |
163 | $this->content->items = array($this->page->navigation); | |
4ca6cfbf | 164 | |
3ea5951e | 165 | $reloadlink = new moodle_url($this->page->url, array('regenerate'=>'navigation')); |
17390771 | 166 | |
c63923bd | 167 | $this->content->footer .= $OUTPUT->action_icon($reloadlink, new pix_icon('t/reload', get_string('reload')), null, array('class'=>'customcommand')); |
4ca6cfbf | 168 | |
7d2a0492 | 169 | // Set content generated to true so that we know it has been done |
170 | $this->contentgenerated = true; | |
4f0c2d00 PS |
171 | |
172 | return $this->content; | |
7d2a0492 | 173 | } |
174 | ||
175 | /** | |
176 | * Returns the attributes to set for this block | |
177 | * | |
178 | * This function returns an array of HTML attributes for this block including | |
179 | * the defaults | |
180 | * {@link block_tree->html_attributes()} is used to get the default arguments | |
181 | * and then we check whether the user has enabled hover expansion and add the | |
182 | * appropriate hover class if it has | |
183 | * | |
184 | * @return array An array of HTML attributes | |
185 | */ | |
186 | public function html_attributes() { | |
187 | $attributes = parent::html_attributes(); | |
188 | if (!empty($this->config->enablehoverexpansion) && $this->config->enablehoverexpansion == 'yes') { | |
9d897331 | 189 | $attributes['class'] .= ' block_js_expansion'; |
7d2a0492 | 190 | } |
7d2a0492 | 191 | return $attributes; |
192 | } | |
193 | ||
194 | /** | |
195 | * This function maintains a history of the active pages that a user has visited | |
196 | * and displays it back to the user as part of the navigation structure | |
197 | * | |
198 | * @return bool | |
199 | */ | |
200 | protected function showmyhistory() { | |
a6855934 | 201 | global $USER, $PAGE; |
7d2a0492 | 202 | |
203 | // Create a navigation cache so that we can store the history | |
204 | $cache = new navigation_cache('navigationhistory', 60*60); | |
205 | ||
206 | // If the user isn't logged in or is a guest we don't want to display anything | |
4cdb8d70 | 207 | if (!isloggedin() || isguestuser()) { |
7d2a0492 | 208 | return false; |
209 | } | |
210 | ||
211 | // Check the cache to see if we have loaded my courses already | |
212 | // there is a very good chance that we have | |
213 | if (!$cache->cached('history')) { | |
214 | $cache->history = array(); | |
215 | } | |
216 | $history = $cache->history; | |
217 | $historycount = count($history); | |
218 | ||
219 | // Find the initial active node | |
220 | $child = false; | |
221 | if ($PAGE->navigation->contains_active_node()) { | |
222 | $child = $PAGE->navigation->find_active_node(); | |
223 | } else if ($PAGE->settingsnav->contains_active_node()) { | |
224 | $child = $PAGE->settingsnav->find_active_node(); | |
225 | } | |
226 | // Check that we found an active child node | |
227 | if ($child!==false) { | |
228 | $properties = array(); | |
229 | // Check whether this child contains another active child node | |
230 | // this can happen if we are looking at a module | |
231 | if ($child->contains_active_node()) { | |
232 | $titlebits = array(); | |
233 | // Loop while the child contains active nodes and in each iteration | |
234 | // find the next node in the correct direction | |
235 | while ($child!==null && $child->contains_active_node()) { | |
236 | if (!empty($child->shorttext)) { | |
237 | $titlebits[] = $child->shorttext; | |
238 | } else { | |
239 | $titlebits[] = $child->text; | |
240 | } | |
241 | foreach ($child->children as $child) { | |
242 | if ($child->contains_active_node() || $child->isactive) { | |
243 | // We have found the active child or one of its parents | |
244 | // so break the foreach so we can proceed in the while | |
245 | break; | |
246 | } | |
247 | } | |
248 | } | |
249 | if (!empty($child->shorttext)) { | |
250 | $titlebits[] = $child->shorttext; | |
251 | } else { | |
252 | $titlebits[] = $child->text; | |
253 | } | |
254 | $properties['text'] = join(' - ', $titlebits); | |
255 | $properties['shorttext'] = join(' - ', $titlebits); | |
256 | } else { | |
257 | $properties['text'] = $child->text; | |
258 | $properties['shorttext'] = $child->shorttext; | |
259 | } | |
260 | $properties['action'] = $child->action; | |
261 | $properties['key'] = $child->key; | |
262 | $properties['type'] = $child->type; | |
263 | $properties['icon'] = $child->icon; | |
264 | ||
265 | // Create a new navigation node object free of the main structure | |
266 | // so that it is easily storeable and customised | |
267 | $child = new navigation_node($properties); | |
268 | ||
269 | // Check that this page isn't already in the history array. If it is | |
270 | // we will remove it so that it gets added at the top and we dont get | |
271 | // duplicate entries | |
272 | foreach ($history as $key=>$node) { | |
273 | if ($node->key == $child->key && $node->type == $child->type) { | |
274 | if ($node->action instanceof moodle_url && $child->action instanceof moodle_url && $node->action->compare($child->action)) { | |
275 | unset($history[$key]); | |
eb788065 | 276 | } else if ($child->action instanceof moodle_url && $child->action->out_omit_querystring() == $node->action) { |
7d2a0492 | 277 | unset($history[$key]); |
278 | } else if ($child->action == $node->action) { | |
279 | unset($history[$key]); | |
280 | } | |
281 | } | |
282 | } | |
283 | // If there is more than 5 elements in the array remove the first one | |
284 | // We want a fifo array | |
285 | if (count($history) > 5) { | |
286 | array_shift($history); | |
287 | } | |
288 | $child->nodetype = navigation_node::NODETYPE_LEAF; | |
289 | $child->children = array(); | |
290 | // Add the child to the history array | |
291 | array_push($history,$child); | |
292 | } | |
293 | ||
294 | // If we have `more than nothing` in the history display it :D | |
295 | if ($historycount > 0) { | |
296 | // Add a branch to hold the users history | |
7a7e209d SH |
297 | $mymoodle = $PAGE->navigation->get('mymoodle', navigation_node::TYPE_CUSTOM); |
298 | $myhistorybranch = $mymoodle->add(get_string('showmyhistorytitle', $this->blockname), null, navigation_node::TYPE_CUSTOM, null, 'myhistory'); | |
299 | $mymoodle->get($myhistorybranch)->children = array_reverse($history); | |
7d2a0492 | 300 | } |
301 | ||
302 | // Cache the history (or update the cached history as it is) | |
303 | $cache->history = $history; | |
304 | ||
305 | return true; | |
306 | } | |
307 | ||
308 | /** | |
309 | * This function loads the users my courses array into the navigation | |
310 | * | |
311 | * @return bool | |
312 | */ | |
313 | protected function showmycourses() { | |
314 | global $USER, $PAGE; | |
315 | ||
316 | // Create a navigation cache to point at the same node as the main navigation | |
317 | // cache | |
318 | $cache = new navigation_cache('navigation'); | |
319 | ||
320 | // If the user isn't logged in or is a guest we don't want to display anything | |
4cdb8d70 | 321 | if (!isloggedin() || isguestuser()) { |
7d2a0492 | 322 | return false; |
323 | } | |
324 | ||
325 | // Check the cache to see if we have loaded my courses already | |
326 | // there is a very good chance that we have | |
327 | if (!$cache->cached('mycourses')) { | |
328 | $cache->mycourses = get_my_courses($USER->id); | |
329 | } | |
330 | $courses = $cache->mycourses; | |
331 | ||
332 | // If no courses to display here, return before adding anything | |
333 | if (!is_array($courses) || count($courses)==0) { | |
334 | return false; | |
335 | } | |
336 | ||
337 | // Add a branch labelled something like My Courses | |
7a7e209d SH |
338 | $mymoodle = $PAGE->navigation->get('mymoodle', navigation_node::TYPE_CUSTOM); |
339 | $mycoursesbranch = $mymoodle->add(get_string('mycourses'), null,navigation_node::TYPE_CATEGORY, null, 'mycourses'); | |
340 | $PAGE->navigation->add_courses($courses, 'mycourses'); | |
341 | $mymoodle->get($mycoursesbranch)->type = navigation_node::TYPE_CUSTOM; | |
7d2a0492 | 342 | return true; |
343 | } | |
344 | ||
345 | /** | |
346 | * This function searches all branches and removes any empty section branches | |
347 | * for the global navigation structure. This is usually called by the global | |
348 | * navigation block based on a block setting | |
349 | */ | |
350 | protected function remove_empty_section_branches() { | |
351 | global $PAGE; | |
352 | $cache = new navigation_cache('navigation'); | |
353 | $course = &$PAGE->navigation->find_active_node(navigation_node::TYPE_COURSE); | |
354 | if ($course===false || !$cache->cached('modinfo'.$course->key) || !$cache->cached('coursesections'.$course->key)) { | |
355 | return 0; | |
356 | } | |
357 | $sectionstoremove = array(); | |
358 | $coursesections = $cache->{'coursesections'.$course->key}; | |
359 | $modinfosections = $cache->{'modinfo'.$course->key}->sections; | |
360 | foreach ($coursesections as $id=>$section) { | |
361 | if (!array_key_exists($id, $modinfosections)) { | |
362 | $sectionstoremove[] = $section->id; | |
363 | } | |
364 | } | |
365 | ||
366 | foreach ($course->children as $key=>$node) { | |
367 | if ($node->type == navigation_node::TYPE_SECTION && in_array($node->key, $sectionstoremove)) { | |
368 | $course->remove_child($key); | |
369 | } | |
370 | } | |
371 | } | |
c1c0cecf | 372 | } |