Note:

If you want to create a new page for developers, you should create it on the Moodle Developer Resource site.

Navigation 2.0 implementation plan

From MoodleDocs

Note: This page is a work-in-progress. Feedback and suggested improvements are welcome. Please join the discussion on moodle.org or use the page comments.

Moodle 2.0


This page lists the various strands of work that will be needed to implement Navigation 2.0 and tried to break them down into manageable chunks.

I think the aim is for me to do as much as this as possible during the 13 weeks I am still working for moodle.com. Therefore, I have focussed on the core work it is necessary to do to achieve the core goals, and then noted ways we could do more later as 'Future ideas'.

Note, all the estimates below should include writing unit tests for the new code, and updateing the corresponding developer documentation on this wiki.

Brief summary in code

Currently, a 'Hello world' Moodle script looks something like:

mod/mymod/helloworld.php: <?php require_once(dirname(__FILE__) . '/../../config.php');

$cmid = required_param('cmid', 0, PARAM_INT); if (!$cm = get_coursemodule_from_id('quiz', $id)) {

   print_error('invalidcoursemodule');

} if (!$course = $DB->get_record('course', array('id' => $cm->course))) {

   print_error('coursemisconf');

}

require_login($course, false, $cm); $context = get_context_instance(CONTEXT_MODULE, $cm->id); require_capability('mod/helloworld:begreeted', $context);

require_js('mod/helloworld/flashyeffects.js');

$title = get_string('greetingtitle', 'helloworld'); $PAGE = page_create_instance($quiz->id); $PAGE->print_header($title, $title); // Does build_navigation($title, $cm)

echo '

'; if (!empty($CFG->showblocksonmodpages) && (blocks_have_content($pageblocks, BLOCK_POS_LEFT) || $PAGE->user_is_editing())) { echo '\n"; } echo ''; if (!empty($CFG->showblocksonmodpages) && (blocks_have_content($pageblocks, BLOCK_POS_RIGHT) || $PAGE->user_is_editing())) { echo ''; } echo '
';
   print_container_start();
   blocks_print_group($PAGE, $pageblocks, BLOCK_POS_LEFT);
   print_container_end();
echo "
';

print_heading(get_string('helloworld', 'helloworld'));

echo '

', get_string('greeting', 'helloworld', fullname($USER)), '

'; echo '
';
   print_container_start();
   blocks_print_group($PAGE, $pageblocks, BLOCK_POS_RIGHT);
   print_container_end();
echo '
'; print_footer($course); In Moodle 2.0, it will look like

Page object

Re-purpose $PAGE. $PAGE becomes the entry point (façade) for a range of services to do with tracking the page we are on.

  • General information - there will be fields
    • $PAGE->course - alias for $COURSE.
    • $PAGE->cm - it we are in an activity, otherwise null.
    • $PAGE->context
    • $PAGE->title - goes into <title> in the HTML
    • $PAGE->url - a moodle_url object for this page.
    • $PAGE->bodyclasses - (MDL-14305, MDL-14306)
    • $PAGE->pagetype - e.g. 'course-view' or 'mod-quiz-attempt'. Same as the id attribute on <body>.
    • $PAGE->generaltype - 'normal', 'popup', etc. (MDL-10681, MDL-12093)
    • $PAGE->docslink -
    • $PAGE->lang
    • $PAGE->theme
  • Other stuff, see below.

Most of this is set up automatically as a result of require_once('config.php'); but could later be overridden by a particular script in special circumstances. Other parts, (e.g. ->course and ->cm) are set up when we have the relevant information, normally as a side-effect of require_login().

Estimates:

  • MDL-12213 Document the sequence in which things get set up during the start of any Moodle page (from require_once('config.php'); to require_login(), or so, showing where all the globals get set up. Both the current flow, and the propsed new order: 2 days
  • MDL-12212, MDL-15817 Based on that implement the basic information storage role of $PAGE, and kill the existing $PAGE subclasses, moving any important code elsewhere: 1 week, but depends on the previous task.

The other roles of $PAGE are covered below, I think.

Themability

See Theme_engines_for_Moodle?.

We will also change all the bits in header.html and footer.html in themes. Instead of them containing code like echo " $bodytags"; // ... if ($navigation) { ?>

} // ... echo $loggedinas; which rely on print_header and print_footer from weblib to put certain things in PHP variables like function print_header(/* ... */, $bodytags, /* ... */) {

   // ...
   $bodytags .= ' class="'.$pageclass.'" id="'.$pageid.'"';
   // ...
   include($CFG->header);
   // ...

}

function print_footer(/* ... */) {

   // ...
   if (!isset($loggedinas)) {
       $loggedinas = user_login_string($usercourse, $USER);
   }
   if ($loggedinas == $menu) {
       $menu = ;
   }
   // ...
   include($CFG->footer);
   // ...  

} irrespective of what the theme wants, instead print_header/footer, which will normally be called as $PAGE->theme->print_header(), will do little more than.

   global $OUTPUT;
   $OUTPUT = $this->get_renderer('moodle');
   // ...
   include($this->get_path() . 'header.html');

and then in header.html and footer.html you will have things like <body class="<?php echo $PAGE->htmlclass; ?>" id="<?php echo $PAGE->pagetype; ?>" ... > // ...

// ... <?php echo $OUTPUT->loggedinas(); ?> That is, some low level facts will be pulled directly from $PAGE, which a renderer will be used to output larger common constructs. (Although, of course, the theme will be welcome to do something completely different.)

In order to ensure backwards-compatibility, we will have to build all of the standard variables as we currently do, unless the theme config.php defines a variable like $doesnotneedlegacyvariables = true;

Estimates:

  • Devise a consistent way to manage the parameter lists of all the print_xxx methods in weblib.php, so that we don't have to have functions with millions of parameters: 1 day.
  • New moodle_core_renderer class and a global instance $OUTPUT, and make deprecated versions of all the old weblib.php functions that delegate to it. 1+ week
  • Implement default_renderer_factory, with the option for themes to specify a different renderer factory in their config.php. The current renderer factory will be accessible as $PAGE->theme, as in $PAGE->theme->get_renderer('forum'); 1- week
  • Create moodle_custom_corners_renderer to get the custom corners mess out of moodle_core_renderer, and to prove that bit of the concept. 1 day
  • MDL-3625, MDL-3626, MDL-14539, MDL-15400 Add standard header and footer bits to moodle_core_renderer and rework header.html and footer.html in the core themes to use it: 1 week
  • Implement a very simple version of themeengine/phptemplates, as a proof of concept. 1- week

Note: need to get tracker issue numbers from Navigation_2.0#Theme_related, or create new issues as needed.

Future ideas:

  • Convert a couple of other parts of moodle to start using a renderer class, to prove the concept. Perhaps just forum post, and a block. We should pick things that are not too difficult, and things that themers are likely to want to customise.
  • Refactor other core plugins to use a renderer class (a bit at a time).
  • Create a theme/standardtemplate.
  • Develop a highly optimised themeengine/phptemplates.

Misc themability issues:

  • MDL-9306 Main course formats need to have tables removed but keep AJAX working - there seem to be some regressions from this work ;-)
    • MDL-12164 Hiding Course Topics and Switching Roles creates Problem in other Topics
    • MDL-13410 Course view misaligned
    • MDL-17450 Display of blocks and topic section not correct in 2.0
  • MDL-10522 Hard-coded <br /> used for spacing.
    • MDL-14058 Code includes
      tags in many places, which breaks theming, or makes it more difficult
  • MDL-15959 patch any theme to have a personal visual style (CSS file) for each course - not sure this is a good solution, but it is an interesting requirement.

Blocks management

Infrastructure

  • New database tables
  • upgrade from existing tables.
  • functions to get the blocks on a page. (MDL-12191,

Blocks editing UI changes

  • New rearrange blocks UI.
  • New settings forms for block using formslib.
  • Detailed sticky blocks UI on block settings form.

Navigation

Note: TODO

  • Navigation information - probably trees of objects that describe how this page fits into the navigation hierarchy. Not quite sure how this will be populated.
    • $PAGE->navbar - a more OO approach to the navigation bar
    • $PAGE->settingspages - behind the scenes links (like roles, settings, filter, files) that relate to this context.
    • $PAGE->globalnav - how this page fits into the global navigation structure.

Estimates:

  • Object oriented approach to the nav bar (MDL-2347, MDL-14061, MDL-14901)
  • Way to set $PAGE->settingspages without duplicating code Probably involves looking at each tabs.php file. 1 week. (MDL-14632)

Miscellaneous revant bugs

See also Navigation_2.0#Block_related.

New blocks

  • New block_tree base class, for blocks that display nested trees. With separate renderer class. (MDL-12183)4 days
  • New settings_block, based on block_tree and taking data from $PAGE->settingspages, replaces admin_tree and admin. 3 days
  • MDL-8369 New navigation block, based on block_tree for displaying global navigation. (MDL-8369) 3 days
  • MDL-16244 Custom menu block (like on moodle.org now).

Convert existing pages

For each existing moodle script:

  • Switch to using the new $PAGE object
  • Take any blocks rendering code out of the page (MDL-18765)
  • Change how the nav bar is built, and the call to print_header/footer
  • Initialise $PAGE->settingspages - possibly from any existing tab bar and update button
  • Convert any require_js calls. Move inline JavaScript out

Probably possible to do several pages per day. However, there seem to be about 600 pages in Moodle that need to be converted.

JavaScript clean-up

  • New class requirements_manager, accessible via $PAGE->requires.
  • Replaces require_js and friends with methods like
    • $PAGE->requires->js('mod/mymod/script.js');
    • $PAGE->requires->css('mod/mymod/styles.css');
    • $PAGE->requires->call_js($fnname, $arguments);
    • $PAGE->requires->string_for_js('answer', 'question');
    • $PAGE->requires->data_for_js(...);
    • $PAGE->requires->skip_link_to('questionbank); (MDL-17730)
  • Normally (apart from CSS) these requirements are output at the foot of the page. To override that and output the associated HTML as soon as possible, you can do
    • $PAGE->requires->js('mod/mymod/script.js')->immediately(); but this is not recommended and normally not necessary.
    • $PAGE->requires->call_js('init_my_feature')->on_dom_ready();
  • requirements_manager tracks what needs to be linked to, and whether a link has been output yet.
  • print_footer and print_header call it get extra HTML to output.
  • keep a deprecated require_js function for backwards compatibility.

There is then just a the small matter of converting all the legacy JS inclusion to user the new API to include itself.

Estimates:

Future ideas:

  • MDL-10932 conditional get support for stylesheets.
  • Extend requirements_manager so it could automatically concatenate CSS and JS into fewer larger files. (This could be done on upgrade and install.) Then serve the appropriate concatenation instead of many separate files, which is better for performance.
  • Investigate open source CSS and JS minifiers, that could be used in in combination with the above.

Filters

Implement Filter enable/disable by context.

  • Create moodle/filter:manage capability.
  • Create DB filter_active table.
  • Upgrade existing $CFG->filters into that table.
  • Update admin page to work with the settings table.
  • New settings page for other contexts.
  • New get_active_filters($context); function.
  • Update caching key in format_text.
  • Backup and restore new settings.

Estimate:

  • MDL-7336 Building the requirements_manager manager class, as above and hooking it up: 1 week Done, will be committed after code review.

Meta

Estimate:

  • Produce these estimates ;-): 2 days (1 day done).

Final TODO: check I have extracting everything important from Navigation/Pagelib/Blocks_2.0_design.

See also

Template:CategoryDeveloper

<?php require_once(dirname(__FILE__) . '/../../config.php'); $cmid = required_param('cmid', 0, PARAM_INT); if (!$cm = get_coursemodule_from_id('quiz', $id)) { print_error('invalidcoursemodule'); } if (!$course = $DB->get_record('course', array('id' => $cm->course))) { print_error('coursemisconf'); } require_login($course, false, $cm); // Fleshes out $PAGE and $OUTPUT $context = $PAGE->context; require_capability('mod/helloworld:begreeted', $context); $PAGE->requires->js('mod/helloworld/flashyeffects.js'); $PAGE->set_title(get_string('greetingtitle', 'helloworld')); // Set $PAGE->navbar /// Some core rendering functions $OUTPUT->header(); $OUTPUT->heading(get_string('helloworld', 'helloworld')); /// Some module-specific rendering functions $helloworldoutput = $PAGE->theme->get_renderer('helloworld'); // uses same convention as get_string() $helloworldoutput->greeting(get_string('greeting', 'helloworld', fullname($USER))); $OUTPUT->footer();

<?php