can a plugin override renderers?

can a plugin override renderers?

by Danny Wahl -
Number of replies: 9

I'd like to write a local plugin that can override a renderer that is not plugin_renderer_base.  Specifically I'd like to define an image using pix_url() and have it overwrite any other instances of that image in output.  Looking at the pix_url() function in outputrenderers it's just a call to a pix_url method in the theme class.  The comments say specifically that themes override plugins, can I change that?

    /**
     * Return the moodle_url for an image.
     *
     * The exact image location and extension is determined
     * automatically by searching for gif|png|jpg|jpeg, please
     * note there can not be diferent images with the different
     * extension. The imagename is for historical reasons
     * a relative path name, it may be changed later for core
     * images. It is recommended to not use subdirectories
     * in plugin and theme pix directories.
     *
     * There are three types of images:
     * 1/ theme images  - stored in theme/mytheme/pix/,
     *                    use component 'theme'
     * 2/ core images   - stored in /pix/,
     *                    overridden via theme/mytheme/pix_core/
     * 3/ plugin images - stored in mod/mymodule/pix,
     *                    overridden via theme/mytheme/pix_plugins/mod/mymodule/,
     *                    example: pix_url('comment', 'mod_glossary')
     *
     * @param string $imagename the pathname of the image
     * @param string $component full plugin name (aka component) or 'theme'
     * @return moodle_url
     */
    public function pix_url($imagename, $component = 'moodle') {
        return $this->page->theme->pix_url($imagename, $component);
    }
Average of ratings: -
In reply to Danny Wahl

Re: can a plugin override renderers?

by Davo Smith -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

Local plugins can't override renderers, it is the theme that has the ability to override renderers.

It is possible to tell your theme to look in a particular local plugin to find a renderer override (but this is still under the control of the theme):

In theme/MYTHEME/config.php: 

require_once($CFG->dirroot.'/local/MYLOCAL/renderer.php');
$THEME->rendererfactory = 'local_MYLOCAL_renderer_factory';

In local/MYLOCAL/renderer.php:

class local_MYLOCAL_renderer_factory extends theme_overridden_renderer_factory {
public function __construct(theme_config $theme) {
  parent::__construct($theme);
  array_unshift($this->prefixes, 'local_MYLOCAL');
}
}

Then you can override the renderer you want to override with 

class local_MYLOCAL_name_of_renderer extends name_of_renderer {
public function XX() {
}
}

However, having said all that - why not just put the overridden image within the theme pix folder, rather than trying to mess around with the code for pix_url?

In reply to Davo Smith

Re: can a plugin override renderers?

by Danny Wahl -

Thanks Davo,

The reason I'm not doing it in the theme is because I'm trying to write a plugin that provides a degree of consistency across all themes, one element of this is setting a favicon in the plugin settings and have it be the same across all themes.  So if I could do something like:

    public function pix_url($imagename, $component = 'moodle') {
        if($imagename === 'favicon') {
            $component = 'local_favicon';
        }
        return $this->page->theme->pix_url($imagename, $component);
    }

or

    public function favicon() {
        return $this->pix_url('favicon', 'local_favicon');
    }

Because almost every theme has a different favicon and updating it across all themes is a) a pain or b) impossible for admins w/o server access.

I'm just stuck at it seeming that plugins can only use plugin_renderer, which I guess I understand for inheritance/sanity but makes what I want to do impossible.

In the future I'd also like to be able to inject additional content into $OUTPUT->standard_head_html() for things like apple-touch-icons which aren't a part of themes at all.

In reply to Danny Wahl

Re: can a plugin override renderers?

by Davo Smith -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

Adding extra stuff to the $OUTPUT->standard_head_html() is easy enough - just set (or add to) $CFG->additionalhtmlhead (see mod/scorm/player.php for an example).

In reply to Davo Smith

Re: can a plugin override renderers?

by Danny Wahl -

Thanks for the pointer, unfortunately I couldn't find any reference to additionalhtml* in all of /mod/scorm/*.  I did look at the 'local_analytics' plugin from the DB and they simply concatenate using lib.php and calling the function itself.  So I have this and it works to manipulate $CFG

function append_additionalhtmlhead() {
    global $CFG;
    
    // Get the current additionalhtmlhead setting
    $additionalhtmlhead = $CFG->additionalhtmlhead . "\n";
    
    // Add favicon link
    $favicon_source = '#'; //pix_url('local_webapp', 'favicon');
    $favicon = '<link rel="shortcut icon" href="' . $favicon_source . '">' . "\n";
    $additionalhtmlhead .= $favicon;
    
    // Replace the original value with our new value
    $CFG->additionalhtmlhead = $additionalhtmlhead;
}
append_additionalhtmlhead();

but if I change $favicon_source to the pix_url(); call I get a fatal error call to unknown function.

In reply to Danny Wahl

Re: can a plugin override renderers?

by Richard Oelmann -
Picture of Core developers Picture of Plugin developers Picture of Testers

What if you locate the original pix_url() function and add a require once for that particular (presumably) lib.php that it is found in, into your function?

In reply to Danny Wahl

Re: can a plugin override renderers?

by Danny Wahl -

I figured it out.  I can't remember why, but I had in another repository this:

$OUTPUT->pix_url('filearea/file','component_name');

and that works.  All I had to do was add global $OUTPUT to my function and call the method not the function. blush

In reply to Davo Smith

Re: can a plugin override renderers?

by Danny Avery -

Hey Davo,

I tried your suggestion but it's not working out. I already have $THEME-renderfactory defined in the theme config, and it appears to be needed, because my theme won't work otherwise. What do you recommend? Thanks!

$THEME->rendererfactory = 'theme_overridden_renderer_factory';

In reply to Danny Avery

Re: can a plugin override renderers?

by Davo Smith -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

The code I suggested still uses the theme's renderers, as well as the local plugin renderer.

If you look at the code, you will notice that it is based on theme_overridden_renderer_factory and the __construct function calls the parent version, so the theme's renderer will be tried as well.

The one issue would be for a renderer that is overridden in both the theme and the local plugin. For that to work as expected, the local version would have to inherit from the theme version of the renderer, not the core version.

e.g.

class local_MYLOCAL_core_renderer extends theme_MYTHEME_core_renderer {

NOT

class local_MYLOCAL_core_renderer extends core_renderer {

(Obviously this only works if you know exactly which theme you are overriding)


In reply to Danny Wahl

Re: can a plugin override renderers?

by Danny Wahl -

I keep coming back to this, scratching my head, and thinking there needs to be a 4th tier in the inheritance or something.  I can think of a number of scenarios where I want to write a plugin that extends the functionality and/or output of another plugin- but which doesn't seem "appropriate" to do in a theme.  In addition, I don't want to do it in a theme because we have multiple themes installed.

Is there room to allow a plugin to override a renderer?  Why can only themes override a renderer?  Here's a couple of ideas to illustrate:

  • A plugin which outputs a specific touch-icon regardless of theme (override $OUTPUT->standardhtmlhead())
  • a plugin which adds "sharing" options to a forum post (override /mod/forum/discuss.php)
  • A plugin which adds/changes javascript (maybe analytics?) to $OUTPUT

All of those, while related to output, are in my opinion beyond the scope of a theme (look and feel of output) but currently can only be modified by a theme (without $CFG hacks... if at all)

Average of ratings: Useful (8)