5d08e1b4da7a5dc5196415cf776b4000d67051c8
[moodle.git] / blocks / navigation / block_navigation.php
1 <?php
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/>.
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  */
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_navigation extends block_base {
39     /** @var int */
40     public static $navcount;
41     /** @var string */
42     public $blockname = null;
43     /** @var bool */
44     protected $contentgenerated = false;
45     /** @var bool|null */
46     protected $docked = null;
48     /**
49      * Set the initial properties for the block
50      */
51     function init() {
52         global $CFG;
53         $this->blockname = get_class($this);
54         $this->title = get_string('pluginname', $this->blockname);
55         $this->version = 2009082800;
56     }
58     /**
59      * All multiple instances of this block
60      * @return bool Returns true
61      */
62     function instance_allow_multiple() {
63         return false;
64     }
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     }
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     }
82     function instance_can_be_docked() {
83         return (parent::instance_can_be_docked() && (empty($this->config->enabledock) || $this->config->enabledock=='yes'));
84     }
86     function get_required_javascript() {
87         global $CFG;
88         $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')));
89         $this->page->requires->js_module(array('name'=>'block_navigation', 'fullpath'=>'/blocks/navigation/navigation.js', 'requires'=>array('core_dock', 'io', 'node', 'dom', 'event-custom', 'json-parse')));
90         user_preference_allow_ajax_update('docked_block_instance_'.$this->instance->id, PARAM_INT);
91     }
93     /**
94      * Gets the content for this block by grabbing it from $this->page
95      */
96     function get_content() {
97         global $CFG, $OUTPUT;
98         // First check if we have already generated, don't waste cycles
99         if ($this->contentgenerated === true) {
100             return $this->content;
101         }
102         $this->page->requires->yui2_lib('dom');
103         // JS for navigation moved to the standard theme, the code will probably have to depend on the actual page structure
104         // $this->page->requires->js('/lib/javascript-navigation.js');
105         // Navcount is used to allow us to have multiple trees although I dont' know why
106         // you would want to trees the same
108         block_navigation::$navcount++;
110         // Check if this block has been docked
111         if ($this->docked === null) {
112             $this->docked = get_user_preferences('nav_in_tab_panel_globalnav'.block_navigation::$navcount, 0);
113         }
115         // Check if there is a param to change the docked state
116         if ($this->docked && optional_param('undock', null, PARAM_INT)==$this->instance->id) {
117             unset_user_preference('nav_in_tab_panel_globalnav'.block_navigation::$navcount);
118             $url = $this->page->url;
119             $url->remove_params(array('undock'));
120             redirect($url);
121         } else if (!$this->docked && optional_param('dock', null, PARAM_INT)==$this->instance->id) {
122             set_user_preferences(array('nav_in_tab_panel_globalnav'.block_navigation::$navcount=>1));
123             $url = $this->page->url;
124             $url->remove_params(array('dock'));
125             redirect($url);
126         }
128         // Initialise (only actually happens if it hasn't already been done yet
129         $this->page->navigation->initialise();
131         if (!empty($this->config->showmyhistory) && $this->config->showmyhistory=='yes') {
132             $this->showmyhistory();
133         }
135         // Get the expandable items so we can pass them to JS
136         $expandable = array();
137         $this->page->navigation->find_expandable($expandable);
139         // Initialise the JS tree object
140         $module = array('name'=>'block_navigation', 'fullpath'=>'/blocks/navigation/navigation.js', 'requires'=>array('core_dock', 'io', 'node', 'dom', 'event-custom', 'json-parse'));
141         $arguments = array($this->instance->id, array('expansions'=>$expandable, 'instance'=>$this->instance->id, 'candock'=>$this->instance_can_be_docked()));
142         $this->page->requires->js_init_call('M.block_navigation.init_add_tree', $arguments, false, $module);
144         // Grab the items to display
145         $renderer = $this->page->get_renderer('block_navigation');
146         $this->content->text = $renderer->navigation_tree($this->page->navigation);
148         $reloadlink = new moodle_url($this->page->url, array('regenerate'=>'navigation'));
150         $this->content->footer .= $OUTPUT->action_icon($reloadlink, new pix_icon('t/reload', get_string('reload')), null, array('class'=>'customcommand reloadnavigation'));
152         // Set content generated to true so that we know it has been done
153         $this->contentgenerated = true;
155         return $this->content;
156     }
158     /**
159      * Returns the attributes to set for this block
160      *
161      * This function returns an array of HTML attributes for this block including
162      * the defaults
163      * {@link block_tree->html_attributes()} is used to get the default arguments
164      * and then we check whether the user has enabled hover expansion and add the
165      * appropriate hover class if it has
166      *
167      * @return array An array of HTML attributes
168      */
169     public function html_attributes() {
170         $attributes = parent::html_attributes();
171         if (!empty($this->config->enablehoverexpansion) && $this->config->enablehoverexpansion == 'yes') {
172             $attributes['class'] .= ' block_js_expansion';
173         }
174         return $attributes;
175     }
177     /**
178      * This function maintains a history of the active pages that a user has visited
179      * and displays it back to the user as part of the navigation structure
180      *
181      * @return bool
182      */
183     protected function showmyhistory() {
184         global $USER, $PAGE;
186         // Create a navigation cache so that we can store the history
187         $cache = new navigation_cache('navigationhistory', 60*60);
189         // If the user isn't logged in or is a guest we don't want to display anything
190         if (!isloggedin() || isguestuser()) {
191             return false;
192         }
194         // Check the cache to see if we have loaded my courses already
195         // there is a very good chance that we have
196         if (!$cache->cached('history')) {
197             $cache->history = array();
198         }
199         $history = $cache->history;
200         $historycount = count($history);
202         // Find the initial active node
203         $child = false;
204         if ($PAGE->navigation->contains_active_node()) {
205             $child = $PAGE->navigation->find_active_node();
206         } else if ($PAGE->settingsnav->contains_active_node()) {
207             $child = $PAGE->settingsnav->find_active_node();
208         }
209         // Check that we found an active child node
210         if ($child!==false) {
211             $properties = array();
212             // Check whether this child contains another active child node
213             // this can happen if we are looking at a module
214             if ($child->contains_active_node()) {
215                 $titlebits = array();
216                 // Loop while the child contains active nodes and in each iteration
217                 // find the next node in the correct direction
218                 while ($child!==null && $child->contains_active_node()) {
219                     if (!empty($child->shorttext)) {
220                         $titlebits[] = $child->shorttext;
221                     } else {
222                         $titlebits[] = $child->text;
223                     }
224                     foreach ($child->children as $child) {
225                         if ($child->contains_active_node() || $child->isactive) {
226                             // We have found the active child or one of its parents
227                             // so break the foreach so we can proceed in the while
228                             break;
229                         }
230                     }
231                 }
232                 if (!empty($child->shorttext)) {
233                     $titlebits[] = $child->shorttext;
234                 } else {
235                     $titlebits[] = $child->text;
236                 }
237                 $properties['text'] = join(' - ', $titlebits);
238                 $properties['shorttext'] = join(' - ', $titlebits);
239             } else {
240                 $properties['text'] = $child->text;
241                 $properties['shorttext'] = $child->shorttext;
242             }
243             $properties['action'] = $child->action;
244             $properties['key'] = $child->key;
245             $properties['type'] = $child->type;
246             $properties['icon'] = $child->icon;
248             // Create a new navigation node object free of the main structure
249             // so that it is easily storeable and customised
250             $child = new navigation_node($properties);
252             // Check that this page isn't already in the history array. If it is
253             // we will remove it so that it gets added at the top and we dont get
254             // duplicate entries
255             foreach ($history as $key=>$node) {
256                 if ($node->key == $child->key && $node->type == $child->type) {
257                     if ($node->action instanceof moodle_url && $child->action instanceof moodle_url && $node->action->compare($child->action)) {
258                         unset($history[$key]);
259                     } else if ($child->action instanceof moodle_url && $child->action->out_omit_querystring() == $node->action) {
260                         unset($history[$key]);
261                     } else if ($child->action == $node->action) {
262                         unset($history[$key]);
263                     }
264                 }
265             }
266             // If there is more than 5 elements in the array remove the first one
267             // We want a fifo array
268             if (count($history) > 5) {
269                 array_shift($history);
270             }
271             $child->nodetype = navigation_node::NODETYPE_LEAF;
272             $child->children = array();
273             // Add the child to the history array
274             array_push($history,$child);
275         }
277         // If we have `more than nothing` in the history display it :D
278         if ($historycount > 0) {
279             // Add a branch to hold the users history
280             $mymoodle = $PAGE->navigation->get('profile', navigation_node::TYPE_USER);
281             $myhistorybranch = $mymoodle->add(get_string('showmyhistorytitle', $this->blockname), null, navigation_node::TYPE_CUSTOM, null, 'myhistory');
282             foreach (array_reverse($history) as $node) {
283                 $myhistorybranch->children->add($node);
284             }
285         }
287         // Cache the history (or update the cached history as it is)
288         $cache->history = $history;
290         return true;
291     }