Does anyone know how to go about this?
I've read this page but I'm still a bit confused:
http://docs.moodle.org/dev/Output_renderers
I just want some functions that you give values to and get back the appropriate HTML, and which can be overridden to deliver slightly different HTML. How do I tie that in with Moodle's renderer system?
edit: I recalled that tabs had been moved into a renderer recently in MDL-38309, but in that case they'd just got moved into the core_renderer. I suppose I could just do that for the time being but it seems a bit more elegant to have them in a dedicated class.
For example, Bootstrap does alerts in a certain way. There's multiple renderers where I might want to use that same code. So I put it in a class and call it in my Bootstrap theme's renderers. That all works fine.
However, if someone wants to override my theme in a child theme, but still wants to do Bootstrappy things they might also want to use that function, or even slightly tweak that function so that it does something different. How do I structure things so that it's easy for them to do so?
The renderer structure seems like it's in the same area, but it also seems quite tightly tied to the idea of a page, rather than just some functions you call from a library.
Dear David,
To be honest I'm not sure on both accounts pertaining to Moodle. But from an OO point of view, it's a matter of defining them in a common area and making the method's you want to be overrideable to be 'protected'. I hope I'm not telling you to suck eggs.
Or should your functions be within 'renderer.php' as additions to the class?
Cheers,
Gareth
Hi David,
Theme renderers are inherited, so unless they override them, the child themes will automatically get them. I say this from my experience with Decaf - it uses renderers, and child themes pick them up automatically but can override them if necessary. I make use of this for the theme we're actually using here, which is a child of Decaf.
For example, Decaf has:
class theme_decaf_core_renderer extends core_renderer {
and our child theme (uc_decaf) has the following:
class theme_uc_decaf_core_renderer extends theme_decaf_core_renderer {
The child theme's renderer only bothers to override one function, leaving the rest to inherit from theme_decaf_core_renderer.
HTH,
Paul
Having a library of functions that other people can reuse is really nothing special. That is what programming is all about.
So, just create a file like theme/mytheme/utils.php, and put the functions/classes in there. If anyone else wants to use those functions, they can requie_once them, and then call them.
Or, looking ahead to what will happen in Moodle 2.6, group all library into classes, for example a class theme_mytheme_utils stored in theme/mytheme/classes/utils.php. For now you still have to require_once it, but in future, people will be able to use it without an explicit require_once. See https://moodle.org/mod/forum/discuss.php?d=229474.
Of course, it is possible that you might be going to implement a really singificant library of code, that you think does not really belong inside your theme. In that case you could put it in a separate plug-in like local/mycoollib.
Hey...thanks Tim that is really helpful.
Marina Glancy did quite a bit of work with Course renderers. so may be an idea to see how she did it, by looking at the code she made into a function which was then set into course/renderer.php
At present you can only override a renderer. And no need to tell you hw to do that as you know full well how to. But as for starting from scratch, I don't have a clue, but I suspect it's quite involved, and needs certain manipulation of core files pertinent to the renderer you want to create.
Sam Marshal and Tim Hunt would probably give you some pointers, as would Marina Glancy if you were to ask theme.
HTH
Mary
Probably the best example of someone doing this is when sam marsall moved the media player code into a renderer. Let me find that ...
- MDL-29624
- https://github.com/moodle/moodle/commit/ffe4de973b36636c06fdf6a44a872d99de0856b9
- https://github.com/moodle/moodle/commit/daefd6eb9736473720749af4f2bff8ee1ae2a3e7
- https://github.com/moodle/moodle/commit/f4e7ba5ea8fbaa97fb2aa0bceef9d9a2271cdc88
- https://github.com/moodle/moodle/commit/8b7d95b6256cbcd912701db09a732880957a785b
The key bit of that is the new renderer at https://github.com/moodle/moodle/commit/daefd6eb9736473720749af4f2bff8ee1ae2a3e7#L12R2915.
By the way, if you don't know about that, then you might want to take a look. It lets you completely change which media player is used from within your theme.
So, having given a particular example, let me revise the theory of how renderers are supposed to work:
1. The purpose of a renderer is to "some functions that you give values to and get back the appropriate HTML, and which can be overridden to deliver slightly different HTML" - so David is definitely asking the right question.
2. A renderer is defined in one place, and then overridden by (parent or child) themes.
3. The place that defines a renderer is a plugin, or moodle core. In fact, a plugin can define several different renderers if it likes. For example, we have core_renderer, core_media_renderer and core_question renderer. Quiz has only mod_quiz_renderer at the moment, but at some point in the future we might add another renderer like mod_quiz_editing_renderer just to keep the code for the teacher UI separate from the student UI.
4. Deciding whether to put all the methods in one renderer, or break it up into smaller logical renderers is really not a big deal. To use the example already given, when tabs were converted to be a renderer, then it was put into core_renderer, but equally it could have been put into a separate renderer core_tabs_renderer. That would work equally well.
5. Having said it really does not matter whether you make one big renderer, or several smaller renderers, each focussed on a particular bit of functionality, I think I would recommend more smaller renderers, to keep specific functionality grouped better. However, this is a matter of taste.
OK, I think that is enough background theory, but now we get to the pay-off:
6. A theme is a type of plugin. Therefore, a theme can define its own renderer. That would go into theme/example/renderer.php and it would be called class theme_example_renderer extends plugin renderer. Of course, as recommended above, you might want to generate several renderers for you theme, like theme_example_prettyswirls_renderer and theme_example_slickstripes_renderer, and you can.
7. If you define that renderer, then you can use it, for example in your layout/general.php file:
$renderer = $PAGE->get_renderer('theme_example');
echo $renderer->thingy($values);
or
$renderer = $PAGE->get_renderer('theme_example', 'prettyswirls');
echo $renderer->clockwise_swirl($values);
You might also want to use you renderer within another renderer that you have overridden. For example
class theme_example_mod_quiz_renderer extends mod_quiz_renderer {
public function view_page($values) {
$renderer = $this->page->get_renderer('theme_example');
$output = '';
$output .= $renderer->border_start($values);
$output .= parent::view_page($values);
$output .= $renderer->border_end($values);
return $output;
}
}
8. And like any other renderer, themes could override it, in which case they might define class theme_child_theme_example_renderer extends theme_example_renderer. That class would be defined in theme/child/renderers.php.
Have I made that clear? If not, please ask.
Just in case that was clear, and you are not yet confused, can I point out a theoretic, but probably totally useless possibility:
It is not just theme_child that could override a renderer defined by theme_example. theme_example is allowed to override its own renderers. So, if you define
In theme/example/renderer.php
class theme_example_renderer extends plugin_renderer_base {
// ...
}
Then,
In theme/example/renderers.php you can define
class theme_example_theme_example_renderer extends theme_example_renderer {
// ...
}
As I say, this is almost entirely useless, but it is a test of whether you really understand how renderers work
I'm still following
Obviously I must try harder to be confusing.
I am now waiting for someone to create theme_theme_theme_theme_theme_renderer.
Tim,
Would it be possible to create a renderer that changed the Home link in the Navbar/breadcrumb do something else, like go to a sister site? If so where would I start?
I've been searching Moodle using git grep and have found lots of interesting things but nothing that gives me the HTML that forms the Home link in the navbar.
Cheers
Mary
I don't think you would create a renderer to solve that problem.
Remember: a renderer is "some functions that you give values to and get back the appropriate HTML".
Changing the destination of the Home link is not that.
However, you can abuse renderers to change the destination of the home link. Something like:
class theme_hacky_core_renderer extends core_renderer {
public function navbar() {
global $PAGE;
$out = parent::navbar();
$out = preg_replace( ... , $out);
return $out;
}
}
(There are other, even more hacky ways to subvert the navigation stuff from a renderer.)
Thanks, I'll give that a try.