navigation MDL-14632 Fix: Dock now respects block weight
[moodle.git] / blocks / moodleblock.class.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 the parent class for moodle blocks, block_base.
20  *
21  * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
22  * @package blocks
23  */
25 /// Constants
27 /**
28  * Block type of list. Contents of block should be set as an associative array in the content object as items ($this->content->items). Optionally include footer text in $this->content->footer.
29  */
30 define('BLOCK_TYPE_LIST',    1);
32 /**
33  * Block type of text. Contents of block should be set to standard html text in the content object as items ($this->content->text). Optionally include footer text in $this->content->footer.
34  */
35 define('BLOCK_TYPE_TEXT',    2);
36 /**
37  * Block type of tree. $this->content->items is a list of tree_item objects and $this->content->footer is a string.
38  */
39 define('BLOCK_TYPE_TREE',    3);
41 /**
42  * Class for describing a moodle block, all Moodle blocks derive from this class
43  *
44  * @author Jon Papaioannou
45  * @package blocks
46  */
47 class block_base {
49     /**
50      * Internal var for storing/caching translated strings
51      * @var string $str
52      */
53     var $str;
55     /**
56      * The title of the block to be displayed in the block title area.
57      * @var string $title
58      */
59     var $title         = NULL;
61     /**
62      * The type of content that this block creates. Currently support options - BLOCK_TYPE_LIST, BLOCK_TYPE_TEXT
63      * @var int $content_type
64      */
65     var $content_type  = BLOCK_TYPE_TEXT;
67     /**
68      * An object to contain the information to be displayed in the block.
69      * @var stdObject $content
70      */
71     var $content       = NULL;
73     /**
74      * A string generated by {@link _add_edit_controls()} to display block manipulation links when the user is in editing mode.
75      * @var string $edit_controls
76      */
77     var $edit_controls = NULL;
79     /**
80      * The current version that the block type defines.
81      * @var string $version
82      */
83     var $version       = NULL;
85     /**
86      * The initialized instance of this block object.
87      * @var block $instance
88      */
89     var $instance      = NULL;
91     /**
92      * The page that this block is appearing on.
93      * @var moodle_page
94      */
95     public $page       = NULL;
97     /**
98      * This blocks's context.
99      * @var stdClass
100      */
101     public $context    = NULL;
103     /**
104      * An object containing the instance configuration information for the current instance of this block.
105      * @var stdObject $config
106      */
107     var $config        = NULL;
109     /**
110      * How often the cronjob should run, 0 if not at all.
111      * @var int $cron
112      */
114     var $cron          = NULL;
116     static $dockinitialised = false;
118 /// Class Functions
120     /**
121      * The class constructor
122      *
123      */
124     function block_base() {
125         $this->init();
126     }
128     /**
129      * Fake constructor to keep PHP5 happy
130      *
131      */
132     function __construct() {
133         $this->block_base();
134     }
136     /**
137      * Function that can be overridden to do extra cleanup before
138      * the database tables are deleted. (Called once per block, not per instance!)
139      */
140     function before_delete() {
141     }
143     /**
144      * Function that can be overridden to do extra setup after a block instance has been
145      * restored from backup. For example, it may need to alter any dates that the block
146      * stores, if the $restore->course_startdateoffset is set.
147      */
148     function after_restore($restore) {
149     }
151     /**
152      * Enable custom instance data section in backup and restore.
153      *
154      * If return true, then {@link instance_backup()} and
155      * {@link instance_restore()} will be called during
156      * backup/restore routines.
157      *
158      * @return boolean
159      **/
160     function backuprestore_instancedata_used() {
161         return false;
162     }
164     /**
165      * Allows the block class to have a backup routine.  Handy
166      * when the block has its own tables that have foreign keys to
167      * other tables (example: user table).
168      *
169      * Note: at the time of writing this comment, the indent level
170      * for the {@link full_tag()} should start at 5.
171      *
172      * @param resource $bf Backup File
173      * @param object $preferences Backup preferences
174      * @return boolean
175      **/
176     function instance_backup($bf, $preferences) {
177         return true;
178     }
180     /**
181      * Allows the block class to restore its backup routine.
182      *
183      * Should not return false if data is empty
184      * because old backups would not contain block instance backup data.
185      *
186      * @param object $restore Standard restore object
187      * @param object $data Object from backup_getid for this block instance
188      * @return boolean
189      **/
190     function instance_restore($restore, $data) {
191         return true;
192     }
194     /**
195      * Will be called before an instance of this block is backed up, so that any links in
196      * in config can be encoded. For example config->text, for the HTML block
197      * @return string
198      */
199     function get_backup_encoded_config() {
200         return base64_encode(serialize($this->config));
201     }
203     /**
204      * Return the content encoded to support interactivities linking. This function is
205      * called automatically from the backup procedure by {@link backup_encode_absolute_links()}.
206      *
207      * NOTE: There is no block instance when this method is called.
208      *
209      * @param string $content Content to be encoded
210      * @param object $restore Restore preferences object
211      * @return string The encoded content
212      **/
213     function encode_content_links($content, $restore) {
214         return $content;
215     }
217     /**
218      * This function makes all the necessary calls to {@link restore_decode_content_links_worker()}
219      * function in order to decode contents of this block from the backup
220      * format to destination site/course in order to mantain inter-activities
221      * working in the backup/restore process.
222      *
223      * This is called from {@link restore_decode_content_links()} function in the restore process.
224      *
225      * NOTE: There is no block instance when this method is called.
226      *
227      * @param object $restore Standard restore object
228      * @return boolean
229      **/
230     function decode_content_links_caller($restore) {
231         return true;
232     }
234     /**
235      * Return content decoded to support interactivities linking.
236      * This is called automatically from
237      * {@link restore_decode_content_links_worker()} function
238      * in the restore process.
239      *
240      * NOTE: There is no block instance when this method is called.
241      *
242      * @param string $content Content to be dencoded
243      * @param object $restore Restore preferences object
244      * @return string The dencoded content
245      **/
246     function decode_content_links($content, $restore) {
247         return $content;
248     }
250     /**
251      * Returns the block name, as present in the class name,
252      * the database, the block directory, etc etc.
253      *
254      * @return string
255      */
256     function name() {
257         // Returns the block name, as present in the class name,
258         // the database, the block directory, etc etc.
259         static $myname;
260         if ($myname === NULL) {
261             $myname = strtolower(get_class($this));
262             $myname = substr($myname, strpos($myname, '_') + 1);
263         }
264         return $myname;
265     }
267     /**
268      * Parent class version of this function simply returns NULL
269      * This should be implemented by the derived class to return
270      * the content object.
271      *
272      * @return stdObject
273      */
274     function get_content() {
275         // This should be implemented by the derived class.
276         return NULL;
277     }
279     /**
280      * Returns the class $title var value.
281      *
282      * Intentionally doesn't check if a title is set.
283      * This is already done in {@link _self_test()}
284      *
285      * @return string $this->title
286      */
287     function get_title() {
288         // Intentionally doesn't check if a title is set. This is already done in _self_test()
289         return $this->title;
290     }
292     /**
293      * Returns the class $content_type var value.
294      *
295      * Intentionally doesn't check if content_type is set.
296      * This is already done in {@link _self_test()}
297      *
298      * @return string $this->content_type
299      */
300     function get_content_type() {
301         // Intentionally doesn't check if a content_type is set. This is already done in _self_test()
302         return $this->content_type;
303     }
305     /**
306      * Returns the class $version var value.
307      *
308      * Intentionally doesn't check if a version is set.
309      * This is already done in {@link _self_test()}
310      *
311      * @return string $this->version
312      */
313     function get_version() {
314         // Intentionally doesn't check if a version is set. This is already done in _self_test()
315         return $this->version;
316     }
318     /**
319      * Returns true or false, depending on whether this block has any content to display
320      * and whether the user has permission to view the block
321      *
322      * @return boolean
323      */
324     function is_empty() {
325         if ( !has_capability('moodle/block:view', $this->context) ) {
326             return true;
327         }
329         $this->get_content();
330         return(empty($this->content->text) && empty($this->content->footer));
331     }
333     /**
334      * First sets the current value of $this->content to NULL
335      * then calls the block's {@link get_content()} function
336      * to set its value back.
337      *
338      * @return stdObject
339      */
340     function refresh_content() {
341         // Nothing special here, depends on content()
342         $this->content = NULL;
343         return $this->get_content();
344     }
346     /**
347      * Return a block_contents oject representing the full contents of this block.
348      *
349      * This internally calls ->get_content(), and then adds the editing controls etc.
350      *
351      * You probably should not override this method, but instead override
352      * {@link html_attributes()}, {@link formatted_contents()} or {@link get_content()},
353      * {@link hide_header()}, {@link (get_edit_controls)}, etc.
354      *
355      * @return block_contents a represntation of the block, for rendering.
356      * @since Moodle 2.0.
357      */
358     public function get_content_for_output($output) {
359         global $CFG;
361         $bc = new block_contents($this->html_attributes());
363         $bc->blockinstanceid = $this->instance->id;
364         $bc->blockpositionid = $this->instance->blockpositionid;
366         if ($this->instance->visible) {
367             $bc->content = $this->formatted_contents($output);
368             if (!empty($this->content->footer)) {
369                 $bc->footer = $this->content->footer;
370             }
371         } else {
372             $bc->add_class('invisible');
373         }
375         if (!$this->hide_header()) {
376             $bc->title = $this->title;
377         }
379         if ($this->page->user_is_editing()) {
380             $bc->controls = $this->page->blocks->edit_controls($this);
381         }
383         if ($this->is_empty() && !$bc->controls) {
384             return null;
385         }
387         if (empty($CFG->allowuserblockhiding) ||
388                 (empty($bc->content) && empty($bc->footer))) {
389             $bc->collapsible = block_contents::NOT_HIDEABLE;
390         } else if (get_user_preferences('block' . $bc->blockinstanceid . 'hidden', false)) {
391             $bc->collapsible = block_contents::HIDDEN;
392         } else {
393             $bc->collapsible = block_contents::VISIBLE;
394         }
396         $bc->annotation = ''; // TODO MDL-19398 need to work out what to say here.
398         return $bc;
399     }
401     /**
402      * Convert the contents of the block to HTML.
403      *
404      * This is used by block base classes like block_list to convert the structured
405      * $this->content->list and $this->content->icons arrays to HTML. So, in most
406      * blocks, you probaby want to override the {@link get_contents()} method,
407      * which generates that structured representation of the contents.
408      *
409      * @param $output The core_renderer to use when generating the output.
410      * @return string the HTML that should appearn in the body of the block.
411      * @since Moodle 2.0.
412      */
413     protected function formatted_contents($output) {
414         $this->get_content();
415         if (!empty($this->content->text)) {
416             $this->get_required_javascript();
417             return $this->content->text;
418         } else {
419             return '';
420         }
421     }
423     /**
424      * Tests if this block has been implemented correctly.
425      * Also, $errors isn't used right now
426      *
427      * @return boolean
428      */
430     function _self_test() {
431         // Tests if this block has been implemented correctly.
432         // Also, $errors isn't used right now
433         $errors = array();
435         $correct = true;
436         if ($this->get_title() === NULL) {
437             $errors[] = 'title_not_set';
438             $correct = false;
439         }
440         if (!in_array($this->get_content_type(), array(BLOCK_TYPE_LIST, BLOCK_TYPE_TEXT, BLOCK_TYPE_TREE))) {
441             $errors[] = 'invalid_content_type';
442             $correct = false;
443         }
444         //following selftest was not working when roles&capabilities were used from block
445 /*        if ($this->get_content() === NULL) {
446             $errors[] = 'content_not_set';
447             $correct = false;
448         }*/
449         if ($this->get_version() === NULL) {
450             $errors[] = 'version_not_set';
451             $correct = false;
452         }
454         $formats = $this->applicable_formats();
455         if (empty($formats) || array_sum($formats) === 0) {
456             $errors[] = 'no_formats';
457             $correct = false;
458         }
460         $width = $this->preferred_width();
461         if (!is_int($width) || $width <= 0) {
462             $errors[] = 'invalid_width';
463             $correct = false;
464         }
465         return $correct;
466     }
468     /**
469      * Subclasses should override this and return true if the
470      * subclass block has a config_global.html file.
471      *
472      * @return boolean
473      */
474     function has_config() {
475         return false;
476     }
478     /**
479      * Default behavior: save all variables as $CFG properties
480      * You don't need to override this if you 're satisfied with the above
481      *
482      * @param array $data
483      * @return boolean
484      */
485     function config_save($data) {
486         foreach ($data as $name => $value) {
487             set_config($name, $value);
488         }
489         return true;
490     }
492     /**
493      * Which page types this block may appear on.
494      *
495      * The information returned here is processed by the
496      * {@link blocks_name_allowed_in_format()} function. Look there if you need
497      * to know exactly how this works.
498      *
499      * Default case: everything except mod and tag.
500      *
501      * @return array page-type prefix => true/false.
502      */
503     function applicable_formats() {
504         // Default case: the block can be used in courses and site index, but not in activities
505         return array('all' => true, 'mod' => false, 'tag' => false);
506     }
509     /**
510      * Default return is false - header will be shown
511      * @return boolean
512      */
513     function hide_header() {
514         return false;
515     }
517     /**
518      * Return any HTML attributes that you want added to the outer <div> that
519      * of the block when it is output.
520      *
521      * Because of the way certain JS events are wired it is a good idea to ensure
522      * that the default values here still get set.
523      * I found the easiest way to do this and still set anything you want is to
524      * override it within your block in the following way
525      *
526      * <code php>
527      * function html_attributes() {
528      *    $attributes = parent::html_attributes();
529      *    $attributes['class'] .= ' mynewclass';
530      *    return $attributes;
531      * }
532      * </code>
533      *
534      * @return array attribute name => value.
535      */
536     function html_attributes() {
537         $attributes = array(
538             'id' => 'inst' . $this->instance->id,
539             'class' => 'block_' . $this->name(). '  sideblock'
540         );
541         if ($this->instance_can_be_docked() && get_user_preferences('docked_block_instance_'.$this->instance->id, 0)) {
542             $attributes['class'] .= ' dock_on_load';
543         }
544         return $attributes;
545     }
547     /**
548      * Set up a particular instance of this class given data from the block_insances
549      * table and the current page. (See {@link block_manager::load_blocks()}.)
550      *
551      * @param stdClass $instance data from block_insances, block_positions, etc.
552      * @param moodle_page $the page this block is on.
553      */
554     function _load_instance($instance, $page) {
555         if (!empty($instance->configdata)) {
556             $this->config = unserialize(base64_decode($instance->configdata));
557         }
558         $this->instance = $instance;
559         $this->context = get_context_instance(CONTEXT_BLOCK, $instance->id);
560         $this->page = $page;
561         $this->specialization();
562     }
564     function get_required_javascript() {
565         $this->_initialise_dock();
566         if ($this->instance_can_be_docked() && !$this->hide_header()) {
567             $this->page->requires->js_init_call('M.core_dock.init_genericblock', array($this->instance->id));
568             user_preference_allow_ajax_update('docked_block_instance_'.$this->instance->id, PARAM_INT);
569         }
570     }
572     /**
573      * This function is called on your subclass right after an instance is loaded
574      * Use this function to act on instance data just after it's loaded and before anything else is done
575      * For instance: if your block will have different title's depending on location (site, course, blog, etc)
576      */
577     function specialization() {
578         // Just to make sure that this method exists.
579     }
581     /**
582      * Is each block of this type going to have instance-specific configuration?
583      * Normally, this setting is controlled by {@link instance_allow_multiple()}: if multiple
584      * instances are allowed, then each will surely need its own configuration. However, in some
585      * cases it may be necessary to provide instance configuration to blocks that do not want to
586      * allow multiple instances. In that case, make this function return true.
587      * I stress again that this makes a difference ONLY if {@link instance_allow_multiple()} returns false.
588      * @return boolean
589      */
590     function instance_allow_config() {
591         return false;
592     }
594     /**
595      * Are you going to allow multiple instances of each block?
596      * If yes, then it is assumed that the block WILL USE per-instance configuration
597      * @return boolean
598      */
599     function instance_allow_multiple() {
600         // Are you going to allow multiple instances of each block?
601         // If yes, then it is assumed that the block WILL USE per-instance configuration
602         return false;
603     }
605     /**
606      * Default behavior: print the config_instance.html file
607      * You don't need to override this if you're satisfied with the above
608      *
609      * @deprecated since Moodle 2.0.
610      * @return boolean whether anything was done. Blocks should use edit_form.php.
611      */
612     function instance_config_print() {
613         global $CFG, $DB, $OUTPUT;
614         // Default behavior: print the config_instance.html file
615         // You don't need to override this if you're satisfied with the above
616         if (!$this->instance_allow_multiple() && !$this->instance_allow_config()) {
617             return false;
618         }
620         if (is_file($CFG->dirroot .'/blocks/'. $this->name() .'/config_instance.html')) {
621             echo $OUTPUT->box_start('generalbox boxaligncenter blockconfiginstance');
622             include($CFG->dirroot .'/blocks/'. $this->name() .'/config_instance.html');
623             echo $OUTPUT->box_end();
624         } else {
625             notice(get_string('blockconfigbad'), str_replace('blockaction=', 'dummy=', qualified_me()));
626         }
628         return true;
629     }
631     /**
632      * Serialize and store config data
633      */
634     function instance_config_save($data, $nolongerused = false) {
635         global $DB;
636         $DB->set_field('block_instances', 'configdata', base64_encode(serialize($data)),
637                 array('id' => $this->instance->id));
638     }
640     /**
641      * Replace the instance's configuration data with those currently in $this->config;
642      */
643     function instance_config_commit($nolongerused = false) {
644         global $DB;
645         $this->instance_config_save($this->config);
646     }
648     /**
649      * Do any additional initialization you may need at the time a new block instance is created
650      * @return boolean
651      */
652     function instance_create() {
653         return true;
654     }
656     /**
657      * Delete everything related to this instance if you have been using persistent storage other than the configdata field.
658      * @return boolean
659      */
660     function instance_delete() {
661         return true;
662     }
664     /**
665      * Allows the block class to have a say in the user's ability to edit (i.e., configure) blocks of this type.
666      * The framework has first say in whether this will be allowed (e.g., no editing allowed unless in edit mode)
667      * but if the framework does allow it, the block can still decide to refuse.
668      * @return boolean
669      */
670     function user_can_edit() {
671         return has_capability('moodle/block:edit', $this->context);
672     }
674     /**
675      * Allows the block class to have a say in the user's ability to create new instances of this block.
676      * The framework has first say in whether this will be allowed (e.g., no adding allowed unless in edit mode)
677      * but if the framework does allow it, the block can still decide to refuse.
678      * This function has access to the complete page object, the creation related to which is being determined.
679      * @return boolean
680      */
681     function user_can_addto($page) {
682         return has_capability('moodle/block:edit', $page->context);
683     }
685     function get_extra_capabilities() {
686         return array('moodle/block:view', 'moodle/block:edit');
687     }
689     // Methods deprecated in Moodle 2.0 ========================================
691     /**
692      * Default case: the block wants to be 180 pixels wide
693      * @deprecated since Moodle 2.0.
694      * @return int
695      */
696     function preferred_width() {
697         return 180;
698     }
700     /** @deprecated since Moodle 2.0. */
701     function _print_block() {
702         throw new coding_exception('_print_block is no longer used. It was a private ' .
703                 'method of the block class, only for use by the blocks system. You ' .
704                 'should not have been calling it anyway.');
705     }
707     /** @deprecated since Moodle 2.0. */
708     function _print_shadow() {
709         throw new coding_exception('_print_shadow is no longer used. It was a private ' .
710                 'method of the block class, only for use by the blocks system. You ' .
711                 'should not have been calling it anyway.');
712     }
714     /** @deprecated since Moodle 2.0. */
715     function _title_html() {
716         throw new coding_exception('_title_html is no longer used. It was a private ' .
717                 'method of the block class, only for use by the blocks system. You ' .
718                 'should not have been calling it anyway.');
719     }
721     /** @deprecated since Moodle 2.0. */
722     function _add_edit_controls() {
723         throw new coding_exception('_add_edit_controls is no longer used. It was a private ' .
724                 'method of the block class, only for use by the blocks system. You ' .
725                 'should not have been calling it anyway.');
726     }
728     /** @deprecated since Moodle 2.0. */
729     function config_print() {
730         throw new coding_exception('config_print() can no longer be used. Blocks should use a settings.php file.');
731     }
733     public function instance_can_be_docked() {
734         global $CFG;
735         return (!empty($CFG->allowblockstodock) && $this->page->theme->enable_dock);
736     }
738     public function _initialise_dock() {
739         global $CFG;
740         if (!self::$dockinitialised) {
741             $this->page->requires->strings_for_js(array('addtodock','undockitem','undockall'), 'block');
742             self::$dockinitialised = true;
743         }
744     }
746     /** @callback callback functions for comments api */
747     public static function comment_template($options) {
748         $ret = <<<EOD
749 <div class="comment-userpicture">___picture___</div>
750 <div class="comment-content">
751     ___name___ - <span>___time___</span>
752     <div>___content___</div>
753 </div>
754 EOD;
755         return $ret;
756     }
757     public static function comment_permissions($options) {
758         return array('view'=>true, 'post'=>true);
759     }
760     public static function comment_url($options) {
761         return null;
762     }
763     public static function comment_display($comments, $options) {
764         return $comments;
765     }
766     public static function comment_add(&$comments, $options) {
767         return true;
768     }
771 /**
772  * Specialized class for displaying a block with a list of icons/text labels
773  *
774  * The get_content method should set $this->content->items and (optionally)
775  * $this->content->icons, instead of $this->content->text.
776  *
777  * @author Jon Papaioannou
778  * @package blocks
779  */
781 class block_list extends block_base {
782     var $content_type  = BLOCK_TYPE_LIST;
784     function is_empty() {
785         if ( !has_capability('moodle/block:view', $this->context) ) {
786             return true;
787         }
789         $this->get_content();
790         return (empty($this->content->items) && empty($this->content->footer));
791     }
793     protected function formatted_contents($output) {
794         $this->get_content();
795         if (!empty($this->content->items)) {
796             $this->get_required_javascript();
797             return $output->list_block_contents($this->content->icons, $this->content->items);
798         } else {
799             return '';
800         }
801     }
804 /**
805  * Specialized class for displaying a tree menu.
806  *
807  * The {@link get_content()} method involves setting the content of
808  * <code>$this->content->items</code> with an array of {@link tree_item}
809  * objects (these are the top-level nodes). The {@link tree_item::children}
810  * property may contain more tree_item objects, and so on. The tree_item class
811  * itself is abstract and not intended for use, use one of it's subclasses.
812  *
813  * Unlike {@link block_list}, the icons are specified as part of the items,
814  * not in a separate array.
815  *
816  * @author Alan Trick
817  * @package blocks
818  * @internal this extends block_list so we get is_empty() for free
819  */
820 class block_tree extends block_list {
822     /**
823      * @var int specifies the manner in which contents should be added to this
824      * block type. In this case <code>$this->content->items</code> is used with
825      * {@link tree_item}s.
826      */
827     public $content_type = BLOCK_TYPE_TREE;
829     /**
830      * Make the formatted HTML ouput.
831      *
832      * Also adds the required javascript call to the page output.
833      *
834      * @param core_renderer $output
835      * @return string HTML
836      */
837     protected function formatted_contents($output) {
838         // based of code in admin_tree
839         global $PAGE; // TODO change this when there is a proper way for blocks to get stuff into head.
840         static $eventattached;
841         if ($eventattached===null) {
842             $eventattached = true;
843         }
844         if (!$this->content) {
845             $this->content = new stdClass;
846             $this->content->items = array();
847         }
848         $this->get_required_javascript();
849         $this->get_content();
850         $content = $output->tree_block_contents($this->content->items,array('class'=>'block_tree list'));
851         if (isset($this->id) && !is_numeric($this->id)) {
852             $content = $output->box($content, 'block_tree_box', $this->id);
853         }
854         return $content;
855     }