Understanding themes

Understanding themes

by pitou col -
Number of replies: 20

Hello

I try to understand Moodle's themes and how they work.

  • I've made changes directly in the core file /blocks/navigation/renderer.php and it was ok, but the same modifications in a renderer won't show anything.


So, I guess there is something I haven't understood.

  • To create a new theme, I have to declare in config.php the theme's parents ($THEME->parents = array('bootstrapbase'); for the cleantheme for exemple)
  • I can create a renderer directly in the theme directory without creating the all new theme, but I have to declare the parents in config.php, is that true ?
  • Do I have something to do to say to moodle that is has to include this renderer or this one or are all the renderers in the theme directory exploited to create the page ?
  • Why can't I see any renderers effects in the page ?

  • In the two links, I have to override a custom menu to add some linksat the end. As I can't see any modifications in my menu, I was wondering why override the custom menu as we can do it in theme's settings by adding some links in the textarea ? So are the two tutos always up to date to work with my moodle 3.0 ?


Thank you.

Average of ratings: -
In reply to pitou col

Re: Understanding themes

by Richard Oelmann -
Picture of Core developers Picture of Plugin developers Picture of Testers
"I was wondering why override the custom menu as we can do it in theme's settings by adding some links in the textarea"

If all you are doing is adding a few links to your custom menu, then no you don't need to override the renderer, just use the menu setting text block. The renderer option for overriding the menu tends to be for when the theme creator wants to add menu options for every site that uses that theme, rather than rely on an admin having to add them manually on their particular site. If you are simply making changes to the menu for your own site, then just do it in the setting.
In reply to Richard Oelmann

Re: Understanding themes

by pitou col -

OK, thank you for the custom menu.

But when I try it with my clean theme, I don't see any new item in the navigation block.

In reply to pitou col

Re: Understanding themes

by pitou col -

 Hy 

With a new install,  it's now correct, I see this custom menu, but on top horizontal position. 

I wanted to add a link in the navigation block, using the clean theme. All the exemples I've read were about overriding the custom menu, so I guess I can't replace it with navigation block?


In reply to pitou col

Re: Understanding themes

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

The navigation block is also a renderer, so you could probably add menu items in there. However, that is not something I have done previously, so I will need to leave that explanation to someone else unfortunately.

In reply to Richard Oelmann

Re: Understanding themes

by Mary Evans -
Picture of Core developers Picture of Documentation writers Picture of Peer reviewers Picture of Plugin developers Picture of Testers

@Richard

What about the Awesome Bar?

Just a thought?

Mary

In reply to Mary Evans

Re: Understanding themes

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

Ah - for clarity, I have overridden the navigation block renderer, but not for the purpose of adding additional links into the code (the awesomebar code is something I treat as an 'upstream library' but I dont think that adds additional links to the navigation, just represents it differently). While I'm sure I will be able to work this out to help at some point, it wont be for at least a day or two and so I was hoping someone may have already done something like this and be able to help faster than i will be able to smile


In reply to Mary Evans

Re: Understanding themes

by pitou col -
To be sure I understand renderers :

There are cores files, like lib/navigationlib.php that create the navigation nodes.
The functions can be overrided by the renderer block/navigation/renderer.php, only for displaying it in the nav block.
The display can be overrided by theme/mythemename/renderers.php in order to change the display.

At https://docs.moodle.org/dev/Output_renderers#plugin_renderer_base
I see :
class theme_themename_core_renderer extends core_renderer {
I don't find any file for core_renderer. 
https://docs.moodle.org/dev/Output_renderers#plugin_renderer_base explains me that core_renderer is used to render the page beside header and footer, ok.

So am I right to override the blocks/plugin/renderer.php when the function to override is in there, and to override core_renderer when there is no renderers for this function yet ?
Where do I find what this core_renderer contains ?

In reply to pitou col

Re: Understanding themes

by Mary Evans -
Picture of Core developers Picture of Documentation writers Picture of Peer reviewers Picture of Plugin developers Picture of Testers

Hi,

Perhaps I am reading this wrong. When you say you are trying to add a link to the Navigation block, Do you mean you want to

  1. ADD a link inside the "Navigation" block menu or
  2. Do you want to ADD a link in the Top menu which links to the "Navigation" block or
  3. Would you like to see "Navigation" as a menu item in the Top menu?

Also to help understand your questions better, can you paste the menu items you are trying to add to the Custom menu?

Thanks

Mary

In reply to Mary Evans

Re: Understanding themes

by pitou col -

Sorry, I haven't see your post.  And if you don't understand, it's probably my fault, I'll try to be clearer

What I try to do, is to hide or to add links in the navigation blocks, like http://moodle.org for examples.
I'm using clear theme and Moodle 3.0.2.

To do that, I've read all that I've found about themes and renderers in the documentation, but as I can't see any changes, I was wondering about what I've missed,

First of all, I wanted to do the extending theme custom to understand how it works with renderers. But after created the renderers.php, I don't see any changes in my custom menu. So I add a link in the theme parameters (my own menu) and that's ok, I can see it in the custom menu.

This what I have, and as you see, there is no 'mycourses' near 'my own menu' (of course, I'm logged with user profile and courses enrolment).

Custom menu

At this point, I want to understand why I can't override the custom menu with my renderer.


And to you and to Richard : thank you !

In reply to pitou col

Re: Understanding themes

by Mary Evans -
Picture of Core developers Picture of Documentation writers Picture of Peer reviewers Picture of Plugin developers Picture of Testers

Hi,

First can you clarify if you mean Clean theme when you said...

"I'm using clear theme and Moodle 3.0.2

I was just reading through the tutorial that extends the custom menu, and found it quite confusing and in some ways very old. and most probably out of date if you are using Moodle 3.0.2. So it would be helpful if you uploaded the renderer that you are trying to get to work in your theme?

If you are using the 'Clean' theme the 'core_renderer.php' can be located in the classes folder:

 moodle/theme/clean/classes/core_renderer.php

The only renderers in that file are to do with the 'page-header' and the 'logo'.

So you can add other functions provided they are from the outputrenderers.php found in

moodle/lib/outputrenderers.php

You might be better reading the tutorial which explains how to Overriding a renderer in a theme, that's if you have not already seen it?

Cheers

Mary

In reply to Mary Evans

Re: Understanding themes

by pitou col -

Hi


Yes, it was clean theme, sorry.

I've made a fresh install, so all I did was to add a file called renderer.php with the following lines :

class theme_themename_core_renderer extends core_renderer {
     protected function render_custom_menu(custom_menu $menu) {
        $mycourses = $this->page->navigation->get('mycourses');
 if (isloggedin() && $mycourses && $mycourses->has_children()) {
    $branchlabel = get_string('mycourses');
    $branchurl   = new moodle_url('/course/index.php');
    $branchtitle = $branchlabel;
    $branchsort  = 10000;
    $branch = $menu->add($branchlabel, $branchurl, $branchtitle, $branchsort);
     foreach ($mycourses->children as $coursenode) {
        $branch->add($coursenode->get_content(), $coursenode->action, $coursenode->get_title());
    }
}
return parent::render_custom_menu($menu);
    }
}

(it's only the file that's explained in this tuto).

Maybe I have to put this renderers.php file in the theme/clean/classes folder in order to use it ?
For the moment, I've declared it in theme/clean/renderers.php and the file is in theme/clean/renderers

The link you give me above send me to a white page, but I've read the oldest version, of course.
Same problem with this tuto.


Thank you for your help.

In reply to pitou col

Re: Understanding themes

by Mary Evans -
Picture of Core developers Picture of Documentation writers Picture of Peer reviewers Picture of Plugin developers Picture of Testers

Hi,

What you need to do is add the My Courses renderer just above the last bracket on in clean/classes/core_renderer.php

As you will see here is the place to add your code is between line 66 and 67. Where Line.66 ends the last function renderer. And Line.67 ends the page.

https://github.com/lazydaisy/moodle/blob/MOODLE_30_STABLE/theme/clean/classes/core_renderer.php#L66-L67

So add this above Line 67:

    protected function render_custom_menu(custom_menu $menu) {

        $mycourses = $this->page->navigation->get('mycourses');
        if (isloggedin() && $mycourses && $mycourses->has_children()) {
            $branchlabel = get_string('mycourses');
            $branchurl   = new moodle_url('/course/index.php');
            $branchtitle = $branchlabel;
            $branchsort  = 10000;
            $branch = $menu->add($branchlabel, $branchurl, $branchtitle, $branchsort);
            
            foreach ($mycourses->children as $coursenode) {
                $branch->add($coursenode->get_content(), $coursenode->action,
$coursenode->get_title());
            }
        }
        return parent::render_custom_menu($menu);
    }

Hope that helps?

Mary

In reply to Mary Evans

Re: Understanding themes

by pitou col -

Yes, it works !

OK, so I've read somewhere that I had to put my renderers in that folder so he will be use. But if I put another renderer file in that "classes" folder, my renderer is not used.

If I put my code into the core_renderer.php, he's used (I hope I'm clear, sorry) and I am able to hide links in the navigation block.

But I find it's not clean (not the theme, but the method) and it will be clearer to use one file for each kind of renderer, isn't it ?

How can I declare many renderers in that folder ?


Thank you for your help.

I'll try to add a link now to this block : next challenge smile I'll probably make a new post for this.


In reply to pitou col

Re: Understanding themes

by Mary Evans -
Picture of Core developers Picture of Documentation writers Picture of Peer reviewers Picture of Plugin developers Picture of Testers

Hi Pitou,

When you create a renderer using a particular function you must always check to see what class renderer that function is extending. Most of the main renderers will use the class core_renderer. However there are lots of other renderers in Moodle. If you look you will find some in...

Moodle/course/renderer.php  ->  class core_course_renderer extends plugin_renderer_base {

Moodle/course/format/topics/renderer.php -> class format_topics_renderer extends format_section_renderer_base {

This is when it can get complicated as you need to create a renderer.php file for each of the types of renderer classes.

For example you would need these three if you wanted to change some elements found in courses and course/formats, as well as the one you added for the custommenu.

classes /
     core_renderer.php
     core_course_renderer.php

     format_topics_renderer.php

I hope this helps?

I have a theme I am working on if you want to try using it you can.

This is the classes folder...

https://github.com/lazydaisy/gologo/tree/master/classes

And this is a link to the theme https://github.com/lazydaisy/gologo.git

And here is a link to the ZIP file

Cheers

Mary

In reply to Mary Evans

Re: Understanding themes

by pitou col -

Hi,

Thank you for the explanations.

That's what I've done yesterday with a file called renderers.php (not a good file name for this example, it should have called block_navigation_renderer.php). With this file in theme/clean/classes, there where no modification in the page.
But with the same code in 'core_renderer.php' witch already exits, it was working.

So, is it the file name ?


If I understand well, I have only to make one file for each renderer I want to use, isn't it

Thx

In reply to pitou col

Re: Understanding themes

by Mary Evans -
Picture of Core developers Picture of Documentation writers Picture of Peer reviewers Picture of Plugin developers Picture of Testers

Like I said in my other reply:

Moodle/course/renderer.php

 uses =>  class core_course_renderer extends plugin_renderer_base {

so this renderer file name HAS to be

core_course_renderer.php

Moodle/course/format/topics/renderer.php

uses => class format_topics_renderer extends format_section_renderer_base {

so this renderer file name HAS to be

format_topics_renderer.php

In reply to Mary Evans

Re: Understanding themes

by pitou col -

AAAHHHh, smile   I thought I had to override with my renderer the bootstrape base renderer, the one who override the function. So my file was called block_navigation_renderer instead of core_renderer.

Now it seems to be clear.

So, to test it : if I want to override for example the course settings navigation when I'm in my course.
I have to find the class who contains the navigation nodes function, because navigation_node doesn't seem to be the class to use.

I've found the /blocks/settings/renderer.php, with the class block_settings_renderer extends plugin_renderer_base, so I have to call my file theme/clean/classes/block_settings_renderer.php

In this file, here is the code I want to test to see if my renderer is used :

<?php
//*Overriding settings navigation block*/
class theme_clean_course_settings_navigation extends block_settings_renderer {
protected function navigation_node(navigation_node $node, $attrs=array()) {
global $CFG;
foreach ($items as $item) {
if ($item->get_content() == get_string('backup')) {
$item->remove();      }      }
return parent::navigation_node(navigation_node $node, $attrs);   }}

but it doesn't work.

However, this line in the /blocks/settings/renderer.php is working.

if ($item->get_content() == get_string('backup')) {continue;}


Is it because of my file name or did I miss something ?

Thanks


In reply to pitou col

Re: Understanding themes

by Mary Evans -
Picture of Core developers Picture of Documentation writers Picture of Peer reviewers Picture of Plugin developers Picture of Testers

I hope this explains what you are asking, of course you could simplify this using $preg_replace
but that's another ball game.

This is the file name:

block_settings_renderer.php

And this is the contents...

<?php

require_once($CFG->dirroot. '/blocks/settings/renderer.php');

class theme_clean_block_settings_renderer extends block_settings_renderer {

    /**
     * Build the navigation node.
     *
     * @param navigation_node $node the navigation node object.
     * @param array $attrs list of attributes.
     * @param int $depth the depth, default to 1.
     * @return string the navigation node code.
     */
    protected function navigation_node(navigation_node $node, $attrs=array(), $depth = 1) {
        $items = $node->children;

        // exit if empty, we don't want an empty ul element
        if ($items->count()==0) {
            return '';
        }

        // array of nested li elements
        $lis = array();
        $number = 0;
        foreach ($items as $item) {
            $number++;
            if (!$item->display) {
                continue;
            }

            $isbranch = ($item->children->count()>0  || $item->nodetype==navigation_node::NODETYPE_BRANCH);
            $hasicon = (!$isbranch && $item->icon instanceof renderable);

            if ($isbranch) {
                $item->hideicon = true;
            }
            
           if ($item->get_content() == get_string('backup')) {
               $item->remove();
         }        

            $content = $this->output->render($item);

            // this applies to the li item which contains all child lists too
            $liclasses = array($item->get_css_type());
            $liexpandable = array();
            if ($isbranch) {
                $liclasses[] = 'contains_branch';
                if (!$item->forceopen || (!$item->forceopen && $item->collapse) || ($item->children->count() == 0
                        && $item->nodetype == navigation_node::NODETYPE_BRANCH)) {
                    $liexpandable = array('aria-expanded' => 'false');
                } else {
                    $liexpandable = array('aria-expanded' => 'true');
                }
                if ($item->requiresajaxloading) {
                    $liexpandable['data-requires-ajax'] = 'true';
                    $liexpandable['data-loaded'] = 'false';
                }

            } else if ($hasicon) {
                $liclasses[] = 'item_with_icon';
            }
            if ($item->isactive === true) {
                $liclasses[] = 'current_branch';
            }
            $nodetextid = 'label_' . $depth . '_' . $number;
            $liattr = array('class' => join(' ', $liclasses), 'tabindex' => '-1', 'role' => 'treeitem') + $liexpandable;
            // class attribute on the div item which only contains the item content
            $divclasses = array('tree_item');
            if ($isbranch) {
                $divclasses[] = 'branch';
            } else {
                $divclasses[] = 'leaf';
            }
            if (!empty($item->classes) && count($item->classes)>0) {
                $divclasses[] = join(' ', $item->classes);
            }
            $divattr = array('class'=>join(' ', $divclasses));
            if (!empty($item->id)) {
                $divattr['id'] = $item->id;
            }
            $content = html_writer::tag('p', $content, $divattr) . $this->navigation_node($item, array(), $depth + 1);
            if (!empty($item->preceedwithhr) && $item->preceedwithhr===true) {
                $content = html_writer::empty_tag('hr') . $content;
            }
            $liattr['aria-labelledby'] = $nodetextid;
            $content = html_writer::tag('li', $content, $liattr);
            $lis[] = $content;
        }

        if (count($lis)) {
            if (empty($attrs['role'])) {
                $attrs['role'] = 'group';
            }
            return html_writer::tag('ul', implode("\n", $lis), $attrs);
        } else {
            return '';
        }
    }
}
In reply to Mary Evans

Re: Understanding themes

by pitou col -

Thank you for opening my eyes.

I was so close to the good answer, the only thing wrong was in the class declaration, where I put an old definition (past from where ??)

Anyway, it works fine, thanks to you.

I'll continue my tests.

Cheers.

Pitou