Continuing work on boxxie theme
[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;
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}