Using Moodle 3.1
I'm sure this is has been asked before and has been a long standing question across developer forums but would like to know if there has been any development through all these years.
Is there a way to override or extend footer() of outputrenderers.php as part of plugin (AND NOT THEME) in such a way that we make a small change to this function and keep rest of the functionality same?
I'm aware that we can only override renderers in theme but what if we use standard theme & do not want to make a core code hack or modification? Surely there should be some way of doing this within the custom plugin ?
Can we not create a renderer file in the plugin that extends core_renderer class and overrides footer() function to output it's core functionality along with a tiny custom change?
Any pointers or suggestion in this direction would be helpful.
There is a perfectly good plugin type that will do this - its called a theme. You create a child theme that inherits everything from your existing theme and adds in a customised renderer to override the core.
Why create additional complexity by creating another plugin that would clash with that functionality?
That's easy to answer, because I've wanted to do the same in the past.
If you are creating a 3rd-party plugin that will be installed on multiple sites, then asking all of them to use a custom theme, just to add some functionality into the site footer, or to manually patch their chosen theme is not a good distribution method. It also means that your plugin may well get relegated to the 'other' category (no automatic updates, no translation support, etc.), simply because of a 1-line core/theme change that is required for it to work.
In my case, the plugin is adding navigation buttons to the bottom of every page: https://moodle.org/plugins/block_navbuttons (this should work with any theme, but requires a core / theme modification to do so).
I could also imagine plugins that added page tracking functionality in the footer of each page (and probably other types that I've not come across), which you shouldn't have to edit a theme in order to be able to do (or add to every different theme for sites that use multiple themes).
Thanks for your replies Richard & Davo!
@Richard - With all due respect, the reason is precisely what @Davo pointed out which is why this needs to be done as part of plugin & not custom/child theme. Since the footer (core code) change is to get the plugin to work it doesn't make sense to create a child theme altogether just to make a small functionality work.
People in the past have also wanted to do this https://moodle.org/mod/forum/discuss.php?d=258975 so it's not a request to create additional complexity but to resolve things in a simpler manner
This seems to me a very basic inheritance need and I'm sure over the years Moodle guys might have found some way to do this within custom plugin, not theme. I'm sure this would be beneficial for the wider community.
Core renderer -> PluginA override
-> PluginB override
-> Theme C override
Which takes precedence? What happens if they clash - either in functionality or in coding?
This may be what you consider a basic need - it is, I fear, a far more complex issue toactually make happen.
I would suggest your best step would be to search the Tracker to see if there is already an issue for this - if there is, vote for it and comment on it (post the link back here for others who may be interested). If not, create an issue on Tracker, define the requirements, try to get some votes for it, see what the HQ developers say about it, see if you can get someone to come up with a code solution that works in all conceivable scenarios.
I don't think it needs to be that complicated. Basically, you just need to define an order or priority, and the highest priority renderer wins.
(Ouch! that has got more complex than I remembered, because of supporting auto-loaded class names as well as the legacy class names.) Probably easiest to understand what that code does by looking at the unit tests: https://github.com/moodle/moodle/blob/master/lib/tests/outputfactories_test.php#L35
Anyway, all that really needs to be done is to define a policy for what other possible class names to search for, in what order.
And, if anyone wants to experiment with this in their own codebase, then you can do the experiments without changing core code, because a theme can specify which renderer factory to use: https://github.com/moodle/moodle/blob/master/theme/bootstrapbase/config.php#L41. Of course, if someone comes up with a successful experiment, that should go into Moodle core.
If I want the override from plugintype A but plugintypeB further along the chain overrides it? As a site admin I'd want to be sure that didn't happen, so would probably end up manually cloning the renderer into the last in chain plugin anyway - exactly the situation that already exists.
The complexity I was referring to is as much in the managing of the site as in the development of the code (or more) and my personal feeling is that such a development would add to that complexity, not remove it.
@Richard & @Tim - Sorry, I'm a bit lost in the conversation. I understand wanting to override core functionality (like footer) or via outputrenderer won't be an easy task and invites complexity but having to make core code change to get a plugin to work (if you don't intend to use custom theme) also invites scalability & maintenance headaches. So what would you all suggest in this particular scenario ?
Sorry to get back after a while but just wanted to check the activity on this thread. Do we have any alternate solution ?
If the only option is to go down the route of overriding footer() function in custom theme, then it raises another question for me - How to determine which theme to override?
Meaning if we need to install our block plugin across different Moodle installs that are already using their own custom theme then how do we create a plugin that has our
--- custom block functionality
--- AND custom theme_overridden_renderer_factory within that determines the current "theme" (standard OR custom) to override footer()?
I'm hopeful to find an answer to this as I'm sure this could solve the problem for lots of people with similar issue.
A few rough ideas below on possible approach. No problem to move to tracker if a better place there for it - unfort. short on time to look through tracker right now.
- Default behavior: Themes applied to plugins using framework currently defined (site, user, course, sess)
- New capability: Extend theme and plugin frameworks to enable opt-out of default theme behavior from within a plugin.
- If opt-out is set in a plugin:
- Would look somewhere in plugin for renderer class
- Renderer class in plugin would apply only to that plugin
- Plugin would still rely on default theme framework for anything not specifically defined in plugin renderer class
- To the previous points on priorities, perhaps extend the existing $CFG->themeorder capability to include 'plugin' as a new type for prioritization to handle conflicts
- If opt-out enabled in plugin but no renderer class defined, or no functions in renderer class, could fall back to default theme behavior (fatal still possible if something in plugin is relying on specific plugin renderer, but hopefully reduces potential.)
- At global level, could enable/disable via theme settings, similar to what exists already for course- and category- specific themes
- At plugin level, could set opt-out somewhere like settings.php or in a required plugin file
- Core update to enable this could perhaps work similarly to existing course or category theme customization?